eye 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. data/.gitignore +31 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +132 -0
  6. data/Rakefile +18 -0
  7. data/bin/eye +282 -0
  8. data/bin/loader_eye +56 -0
  9. data/examples/processes/em.rb +56 -0
  10. data/examples/processes/forking.rb +20 -0
  11. data/examples/processes/sample.rb +144 -0
  12. data/examples/rbenv.eye +11 -0
  13. data/examples/test.eye +65 -0
  14. data/examples/unicorn.eye +29 -0
  15. data/eye.gemspec +37 -0
  16. data/lib/eye.rb +25 -0
  17. data/lib/eye/application.rb +65 -0
  18. data/lib/eye/checker.rb +118 -0
  19. data/lib/eye/checker/cpu.rb +27 -0
  20. data/lib/eye/checker/file_ctime.rb +29 -0
  21. data/lib/eye/checker/file_size.rb +38 -0
  22. data/lib/eye/checker/http.rb +94 -0
  23. data/lib/eye/checker/memory.rb +27 -0
  24. data/lib/eye/checker/socket.rb +148 -0
  25. data/lib/eye/checker/validation.rb +49 -0
  26. data/lib/eye/child_process.rb +75 -0
  27. data/lib/eye/client.rb +32 -0
  28. data/lib/eye/control.rb +2 -0
  29. data/lib/eye/controller.rb +43 -0
  30. data/lib/eye/controller/commands.rb +64 -0
  31. data/lib/eye/controller/helpers.rb +61 -0
  32. data/lib/eye/controller/load.rb +224 -0
  33. data/lib/eye/controller/send_command.rb +88 -0
  34. data/lib/eye/controller/status.rb +136 -0
  35. data/lib/eye/dsl.rb +52 -0
  36. data/lib/eye/dsl/application_opts.rb +33 -0
  37. data/lib/eye/dsl/chain.rb +12 -0
  38. data/lib/eye/dsl/child_process_opts.rb +7 -0
  39. data/lib/eye/dsl/config_opts.rb +11 -0
  40. data/lib/eye/dsl/group_opts.rb +27 -0
  41. data/lib/eye/dsl/helpers.rb +12 -0
  42. data/lib/eye/dsl/main.rb +58 -0
  43. data/lib/eye/dsl/opts.rb +88 -0
  44. data/lib/eye/dsl/process_opts.rb +21 -0
  45. data/lib/eye/dsl/pure_opts.rb +132 -0
  46. data/lib/eye/dsl/validate.rb +41 -0
  47. data/lib/eye/group.rb +125 -0
  48. data/lib/eye/group/chain.rb +68 -0
  49. data/lib/eye/io/unix_server.rb +44 -0
  50. data/lib/eye/io/unix_socket.rb +39 -0
  51. data/lib/eye/loader.rb +13 -0
  52. data/lib/eye/logger.rb +80 -0
  53. data/lib/eye/process.rb +83 -0
  54. data/lib/eye/process/child.rb +61 -0
  55. data/lib/eye/process/commands.rb +256 -0
  56. data/lib/eye/process/config.rb +70 -0
  57. data/lib/eye/process/controller.rb +72 -0
  58. data/lib/eye/process/data.rb +46 -0
  59. data/lib/eye/process/monitor.rb +97 -0
  60. data/lib/eye/process/notify.rb +17 -0
  61. data/lib/eye/process/scheduler.rb +50 -0
  62. data/lib/eye/process/states.rb +92 -0
  63. data/lib/eye/process/states_history.rb +62 -0
  64. data/lib/eye/process/system.rb +60 -0
  65. data/lib/eye/process/trigger.rb +32 -0
  66. data/lib/eye/process/watchers.rb +67 -0
  67. data/lib/eye/server.rb +51 -0
  68. data/lib/eye/settings.rb +35 -0
  69. data/lib/eye/system.rb +145 -0
  70. data/lib/eye/system_resources.rb +83 -0
  71. data/lib/eye/trigger.rb +53 -0
  72. data/lib/eye/trigger/flapping.rb +24 -0
  73. data/lib/eye/utils.rb +5 -0
  74. data/lib/eye/utils/alive_array.rb +31 -0
  75. data/lib/eye/utils/celluloid_chain.rb +51 -0
  76. data/lib/eye/utils/leak_19.rb +7 -0
  77. data/lib/eye/utils/tail.rb +20 -0
  78. data/spec/checker/cpu_spec.rb +58 -0
  79. data/spec/checker/file_ctime_spec.rb +34 -0
  80. data/spec/checker/file_size_spec.rb +107 -0
  81. data/spec/checker/http_spec.rb +109 -0
  82. data/spec/checker/memory_spec.rb +64 -0
  83. data/spec/checker/socket_spec.rb +116 -0
  84. data/spec/checker_spec.rb +188 -0
  85. data/spec/child_process/child_process_spec.rb +46 -0
  86. data/spec/client_server_spec.rb +34 -0
  87. data/spec/controller/commands_spec.rb +92 -0
  88. data/spec/controller/controller_spec.rb +133 -0
  89. data/spec/controller/find_objects_spec.rb +150 -0
  90. data/spec/controller/group_spec.rb +110 -0
  91. data/spec/controller/intergration_spec.rb +327 -0
  92. data/spec/controller/load_spec.rb +326 -0
  93. data/spec/controller/races_spec.rb +70 -0
  94. data/spec/controller/stop_on_delete_spec.rb +157 -0
  95. data/spec/dsl/chain_spec.rb +140 -0
  96. data/spec/dsl/checks_spec.rb +202 -0
  97. data/spec/dsl/config_spec.rb +44 -0
  98. data/spec/dsl/dsl_spec.rb +73 -0
  99. data/spec/dsl/getter_spec.rb +223 -0
  100. data/spec/dsl/integration_spec.rb +311 -0
  101. data/spec/dsl/load_spec.rb +52 -0
  102. data/spec/dsl/process_spec.rb +330 -0
  103. data/spec/dsl/sub_procs_spec.rb +93 -0
  104. data/spec/dsl/with_server_spec.rb +104 -0
  105. data/spec/example/em.rb +57 -0
  106. data/spec/example/forking.rb +20 -0
  107. data/spec/example/sample.rb +154 -0
  108. data/spec/fixtures/dsl/0.rb +8 -0
  109. data/spec/fixtures/dsl/0a.rb +8 -0
  110. data/spec/fixtures/dsl/0c.rb +8 -0
  111. data/spec/fixtures/dsl/1.rb +5 -0
  112. data/spec/fixtures/dsl/bad.eye +6 -0
  113. data/spec/fixtures/dsl/configs/1.eye +3 -0
  114. data/spec/fixtures/dsl/configs/2.eye +1 -0
  115. data/spec/fixtures/dsl/configs/3.eye +1 -0
  116. data/spec/fixtures/dsl/configs/4.eye +3 -0
  117. data/spec/fixtures/dsl/empty.eye +20 -0
  118. data/spec/fixtures/dsl/include_test.eye +5 -0
  119. data/spec/fixtures/dsl/include_test/1.rb +6 -0
  120. data/spec/fixtures/dsl/include_test/ha.rb +4 -0
  121. data/spec/fixtures/dsl/include_test2.eye +5 -0
  122. data/spec/fixtures/dsl/integration.eye +30 -0
  123. data/spec/fixtures/dsl/integration2.eye +32 -0
  124. data/spec/fixtures/dsl/integration_locks.eye +30 -0
  125. data/spec/fixtures/dsl/integration_sor.eye +32 -0
  126. data/spec/fixtures/dsl/integration_sor2.eye +27 -0
  127. data/spec/fixtures/dsl/integration_sor3.eye +32 -0
  128. data/spec/fixtures/dsl/load.eye +25 -0
  129. data/spec/fixtures/dsl/load2.eye +7 -0
  130. data/spec/fixtures/dsl/load2_dup2.eye +13 -0
  131. data/spec/fixtures/dsl/load2_dup_pid.eye +7 -0
  132. data/spec/fixtures/dsl/load3.eye +10 -0
  133. data/spec/fixtures/dsl/load4.eye +7 -0
  134. data/spec/fixtures/dsl/load5.eye +8 -0
  135. data/spec/fixtures/dsl/load6.eye +17 -0
  136. data/spec/fixtures/dsl/load_dubls.eye +36 -0
  137. data/spec/fixtures/dsl/load_dup_ex_names.eye +15 -0
  138. data/spec/fixtures/dsl/load_error.eye +5 -0
  139. data/spec/fixtures/dsl/load_error_folder/load3.eye +10 -0
  140. data/spec/fixtures/dsl/load_error_folder/load4.eye +7 -0
  141. data/spec/fixtures/dsl/load_folder/load3.eye +10 -0
  142. data/spec/fixtures/dsl/load_folder/load4.eye +7 -0
  143. data/spec/fixtures/dsl/load_int.eye +8 -0
  144. data/spec/fixtures/dsl/load_int2.eye +13 -0
  145. data/spec/fixtures/dsl/load_logger.eye +26 -0
  146. data/spec/fixtures/dsl/load_logger2.eye +3 -0
  147. data/spec/fixtures/dsl/long_load.eye +5 -0
  148. data/spec/fixtures/dsl/subfolder1/proc1.rb +3 -0
  149. data/spec/fixtures/dsl/subfolder2.eye +9 -0
  150. data/spec/fixtures/dsl/subfolder2/common.rb +1 -0
  151. data/spec/fixtures/dsl/subfolder2/proc2.rb +3 -0
  152. data/spec/fixtures/dsl/subfolder2/sub/proc3.rb +6 -0
  153. data/spec/fixtures/dsl/subfolder3.eye +8 -0
  154. data/spec/fixtures/dsl/subfolder3/common.rb +1 -0
  155. data/spec/fixtures/dsl/subfolder3/proc4.rb +3 -0
  156. data/spec/fixtures/dsl/subfolder3/sub/proc5.rb +6 -0
  157. data/spec/fixtures/dsl/subfolder4.eye +6 -0
  158. data/spec/fixtures/dsl/subfolder4/a.rb +2 -0
  159. data/spec/fixtures/dsl/subfolder4/b.rb +1 -0
  160. data/spec/fixtures/dsl/subfolder4/c.rb +1 -0
  161. data/spec/mock_spec.rb +32 -0
  162. data/spec/process/checks/child_checks_spec.rb +79 -0
  163. data/spec/process/checks/cpu_spec.rb +114 -0
  164. data/spec/process/checks/ctime_spec.rb +43 -0
  165. data/spec/process/checks/fsize_spec.rb +22 -0
  166. data/spec/process/checks/http_spec.rb +52 -0
  167. data/spec/process/checks/intergration_spec.rb +32 -0
  168. data/spec/process/checks/memory_spec.rb +113 -0
  169. data/spec/process/child_process_spec.rb +125 -0
  170. data/spec/process/config_spec.rb +75 -0
  171. data/spec/process/controller_spec.rb +173 -0
  172. data/spec/process/monitoring_spec.rb +180 -0
  173. data/spec/process/restart_spec.rb +174 -0
  174. data/spec/process/scheduler_spec.rb +150 -0
  175. data/spec/process/start_spec.rb +261 -0
  176. data/spec/process/states_history_spec.rb +118 -0
  177. data/spec/process/stop_spec.rb +150 -0
  178. data/spec/process/system_spec.rb +100 -0
  179. data/spec/process/triggers/flapping_spec.rb +81 -0
  180. data/spec/process/update_config_spec.rb +63 -0
  181. data/spec/spec_helper.rb +120 -0
  182. data/spec/support/rr_celluloid.rb +36 -0
  183. data/spec/support/scheduler_hack.rb +16 -0
  184. data/spec/support/spec_support.rb +164 -0
  185. data/spec/system_resources_spec.rb +59 -0
  186. data/spec/system_spec.rb +170 -0
  187. data/spec/utils/alive_array_spec.rb +50 -0
  188. data/spec/utils/celluloid_chain_spec.rb +82 -0
  189. data/spec/utils/tail_spec.rb +21 -0
  190. metadata +558 -0
@@ -0,0 +1,65 @@
1
+ class Eye::Application
2
+
3
+ attr_reader :groups, :name
4
+
5
+ include Eye::Logger::Helpers
6
+
7
+ def initialize(name, config = {})
8
+ @groups = Eye::Utils::AliveArray.new
9
+ @name = name
10
+ @logger = Eye::Logger.new(full_name)
11
+ @config = config
12
+ debug 'created'
13
+ end
14
+
15
+ def full_name
16
+ @name
17
+ end
18
+
19
+ def update_config(cfg)
20
+ @config = cfg
21
+ end
22
+
23
+ def add_group(group)
24
+ @groups << group
25
+ end
26
+
27
+ def status_data(debug = false)
28
+ h = { name: @name, type: :application, subtree: @groups.map{|gr| gr.status_data(debug) }}
29
+ h.merge!(debug: debug_data) if debug
30
+ h
31
+ end
32
+
33
+ def status_data_short
34
+ h = Hash.new 0
35
+ @groups.each do |c|
36
+ c.processes.each do |p|
37
+ h[p.state] += 1
38
+ end
39
+ end
40
+ str = h.sort_by{|a,b| a}.map{|k, v| "#{k}:#{v}" } * ', '
41
+ { name: @name, type: :application, state: str}
42
+ end
43
+
44
+ def debug_data
45
+ end
46
+
47
+ def send_command(command, *args)
48
+ debug "send_command #{command} #{args}"
49
+
50
+ @groups.each do |group|
51
+ group.send_command(command, *args)
52
+ end
53
+ end
54
+
55
+ def alive?
56
+ true # emulate celluloid actor method
57
+ end
58
+
59
+ def sub_object?(obj)
60
+ res = @groups.include?(obj)
61
+ res = @groups.any?{|gr| gr.sub_object?(obj)} if !res
62
+ res
63
+ end
64
+
65
+ end
@@ -0,0 +1,118 @@
1
+ class Eye::Checker
2
+ include Eye::Logger::Helpers
3
+
4
+ autoload :Validation, 'eye/checker/validation'
5
+
6
+ autoload :Memory, 'eye/checker/memory'
7
+ autoload :Cpu, 'eye/checker/cpu'
8
+ autoload :Http, 'eye/checker/http'
9
+ autoload :FileCTime, 'eye/checker/file_ctime'
10
+ autoload :FileSize, 'eye/checker/file_size'
11
+ autoload :Socket, 'eye/checker/socket'
12
+
13
+ TYPES = {:memory => "Memory", :cpu => "Cpu", :http => "Http",
14
+ :ctime => "FileCTime", :fsize => "FileSize", :socket => "Socket"}
15
+
16
+ attr_accessor :value, :values, :options, :pid, :type
17
+
18
+ def self.get_class(type)
19
+ klass = eval("Eye::Checker::#{TYPES[type]}") rescue nil
20
+ raise "Unknown checker #{type}" unless klass
21
+ klass
22
+ end
23
+
24
+ def self.create(pid, options = {}, logger_prefix = nil)
25
+ get_class(options[:type]).new(pid, options, logger_prefix)
26
+ end
27
+
28
+ def self.validate!(options)
29
+ get_class(options[:type]).validate(options)
30
+ end
31
+
32
+ def initialize(pid, options = {}, logger_prefix = nil)
33
+ @pid = pid
34
+ @options = options
35
+ @type = options[:type]
36
+
37
+ @logger = Eye::Logger.new(logger_prefix, "check:#{check_name}")
38
+ debug "create checker, with #{options}"
39
+
40
+ @value = nil
41
+ @values = Eye::Utils::Tail.new(max_tries)
42
+ end
43
+
44
+ def last_human_values
45
+ h_values = @values.map do |v|
46
+ sign = v[:good] ? '' : '*'
47
+ sign + human_value(v[:value]).to_s
48
+ end
49
+
50
+ '[' + h_values * ', ' + ']'
51
+ end
52
+
53
+ def check
54
+ @value = get_value
55
+ @values << {:value => @value, :good => good?(value)}
56
+
57
+ result = true
58
+
59
+ if @values.size == max_tries
60
+ bad_count = @values.count{|v| !v[:good] }
61
+ result = false if bad_count >= min_tries
62
+ end
63
+
64
+ info "#{last_human_values} => #{result ? 'OK' : 'Fail'}"
65
+ result
66
+ end
67
+
68
+ def get_value
69
+ raise 'Realize me'
70
+ end
71
+
72
+ def human_value(value)
73
+ value.to_s
74
+ end
75
+
76
+ # true if check ok
77
+ # false if check bad
78
+ def good?(value)
79
+ raise 'Realize me'
80
+ end
81
+
82
+ def check_name
83
+ self.class.to_s
84
+ end
85
+
86
+ def max_tries
87
+ @max_tries ||= if times
88
+ if times.is_a?(Array)
89
+ times[-1].to_i
90
+ else
91
+ times.to_i
92
+ end
93
+ else
94
+ 1
95
+ end
96
+ end
97
+
98
+ def min_tries
99
+ @min_tries ||= if times
100
+ if times.is_a?(Array)
101
+ times[0].to_i
102
+ else
103
+ max_tries
104
+ end
105
+ else
106
+ max_tries
107
+ end
108
+ end
109
+
110
+ def previous_value
111
+ @values[-1][:value] if @values.present?
112
+ end
113
+
114
+ extend Eye::Checker::Validation
115
+ param :every, [Fixnum, Float], false, 5
116
+ param :times, [Fixnum, Array]
117
+
118
+ end
@@ -0,0 +1,27 @@
1
+ class Eye::Checker::Cpu < Eye::Checker
2
+
3
+ # checks :cpu, :every => 3.seconds, :below => 80, :times => [3,5]
4
+
5
+ param :below, [Fixnum, Float], true
6
+
7
+ def check_name
8
+ "cpu(#{human_value(below)})"
9
+ end
10
+
11
+ def get_value
12
+ Eye::SystemResources.cpu(@pid).to_i # nil => 0
13
+ end
14
+
15
+ def human_value(value)
16
+ "#{value}%"
17
+ end
18
+
19
+ def good?(value)
20
+ if below
21
+ value < below
22
+ else
23
+ true
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,29 @@
1
+ class Eye::Checker::FileCTime < Eye::Checker
2
+
3
+ # Check that file changes (log for example)
4
+
5
+ # checks :ctime, :every => 5.seconds, :file => "/tmp/1.log", :times => [3,5]
6
+
7
+ param :file, [String], true
8
+
9
+ def check_name
10
+ 'ctime'
11
+ end
12
+
13
+ def get_value
14
+ File.ctime(file) rescue nil
15
+ end
16
+
17
+ def human_value(value)
18
+ if value == nil
19
+ 'Err'
20
+ else
21
+ value.strftime('%H:%M')
22
+ end
23
+ end
24
+
25
+ def good?(value)
26
+ value.to_i > previous_value.to_i
27
+ end
28
+
29
+ end
@@ -0,0 +1,38 @@
1
+ class Eye::Checker::FileSize < Eye::Checker
2
+
3
+ # Check that file size changed (log for example)
4
+
5
+ # checks :fsize, :every => 5.seconds, :file => "/tmp/1.log", :times => [3,5],
6
+ # :below => 30.kilobytes, :above => 10.kilobytes
7
+
8
+ param :file, [String], true
9
+ param :below, [Fixnum, Float]
10
+ param :above, [Fixnum, Float]
11
+
12
+ def check_name
13
+ 'fsize'
14
+ end
15
+
16
+ def get_value
17
+ File.size(file) rescue nil
18
+ end
19
+
20
+ def human_value(value)
21
+ "#{value.to_i / 1024}Kb"
22
+ end
23
+
24
+ def good?(value)
25
+ return true unless previous_value
26
+
27
+ diff = value.to_i - previous_value.to_i
28
+
29
+ return true if diff < 0 # case when logger nulled
30
+
31
+ return false if below && diff > below
32
+ return false if above && diff < above
33
+ return false if diff == 0
34
+
35
+ true
36
+ end
37
+
38
+ end
@@ -0,0 +1,94 @@
1
+ require 'net/http'
2
+
3
+ class Eye::Checker::Http < Eye::Checker
4
+
5
+ # checks :http, :every => 5.seconds, :times => 1,
6
+ # :url => "http://127.0.0.1:3000/", :kind => :success, :pattern => /OK/, :timeout => 3.seconds
7
+
8
+ param :url, String, true
9
+ param :pattern, [String, Regexp]
10
+ param :kind
11
+ param :timeout, [Fixnum, Float]
12
+ param :open_timeout, [Fixnum, Float]
13
+ param :read_timeout, [Fixnum, Float]
14
+
15
+ attr_reader :session, :uri
16
+
17
+ def check_name
18
+ 'http'
19
+ end
20
+
21
+ def initialize(*args)
22
+ super
23
+
24
+ @uri = URI.parse(url) rescue URI.parse('http://127.0.0.1')
25
+ @pattern = pattern
26
+ @kind = case kind
27
+ when Fixnum then Net::HTTPResponse::CODE_TO_OBJ[kind]
28
+ when String, Symbol then Net.const_get("HTTP#{kind.to_s.camelize}") rescue Net::HTTPSuccess
29
+ else
30
+ Net::HTTPSuccess
31
+ end
32
+ @open_timeout = (open_timeout || timeout || 5).to_i
33
+ @read_timeout = (read_timeout || timeout || 30).to_i
34
+
35
+ @session = Net::HTTP.new(@uri.host, @uri.port)
36
+ if @uri.scheme == 'https'
37
+ require 'net/https'
38
+ @session.use_ssl=true
39
+ @session.verify_mode = OpenSSL::SSL::VERIFY_NONE
40
+ end
41
+ @session.open_timeout = @open_timeout
42
+ @session.read_timeout = @read_timeout
43
+ end
44
+
45
+ def get_value
46
+ Celluloid::Future.new{ get_value_sync }.value
47
+ end
48
+
49
+ def get_value_sync
50
+ res = @session.start do |http|
51
+ http.get(@uri.path)
52
+ end
53
+
54
+ {:result => res}
55
+
56
+ rescue Timeout::Error
57
+ debug 'Timeout error'
58
+ {:exception => :timeout}
59
+
60
+ rescue => ex
61
+ error "Exception #{ex.message}"
62
+ {:exception => ex.message}
63
+ end
64
+
65
+ def good?(value)
66
+ return false unless value[:result]
67
+ return false unless value[:result].kind_of?(@kind)
68
+
69
+ if @pattern
70
+ if @pattern.is_a?(Regexp)
71
+ @pattern === value[:result].body
72
+ else
73
+ value[:result].body.include?(@pattern.to_s)
74
+ end
75
+ else
76
+ true
77
+ end
78
+ end
79
+
80
+ def human_value(value)
81
+ if !value.is_a?(Hash)
82
+ '-'
83
+ elsif value[:exception]
84
+ if value[:exception] == :timeout
85
+ 'T-out'
86
+ else
87
+ 'Err'
88
+ end
89
+ else
90
+ "#{value[:result].code}=#{value[:result].body.size/ 1024}Kb"
91
+ end
92
+ end
93
+
94
+ end
@@ -0,0 +1,27 @@
1
+ class Eye::Checker::Memory < Eye::Checker
2
+
3
+ # checks :memory, :every => 3.seconds, :below => 80.megabytes, :times => [3,5]
4
+
5
+ param :below, [Fixnum, Float], true
6
+
7
+ def check_name
8
+ "memory(#{human_value(below)})"
9
+ end
10
+
11
+ def get_value
12
+ Eye::SystemResources.memory(@pid).to_i * 1024
13
+ end
14
+
15
+ def human_value(value)
16
+ "#{value.to_i / 1024 / 1024}Mb"
17
+ end
18
+
19
+ def good?(value)
20
+ if below
21
+ value < below
22
+ else
23
+ true
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,148 @@
1
+ class Eye::Checker::Socket < Eye::Checker
2
+
3
+ # checks :socket, :every => 5.seconds, :times => 1,
4
+ # :addr => "unix:/var/run/daemon.sock", :timeout => 3.seconds,
5
+ #
6
+ # Available parameters:
7
+ # :addr the socket addr to open. The format is tcp://<host>:<port> or unix:<path>
8
+ # :timeout generic timeout for opening the socket or reading data
9
+ # :open_timeout override generic timeout for the connection
10
+ # :read_timeout override generic timeout for data read/write
11
+ # :send_data after connection send this data
12
+ # :expect_data after sending :send_data expect this response. Can be a string, Regexp or a Proc
13
+ # :protocol way of pack,unpack messages (default = socket default), example: :protocol => :em_object
14
+
15
+ param :addr, String, true
16
+ param :timeout, [Fixnum, Float]
17
+ param :open_timeout, [Fixnum, Float]
18
+ param :read_timeout, [Fixnum, Float]
19
+ param :send_data
20
+ param :expect_data, [String, Regexp, Proc]
21
+ param :protocol, [Symbol]
22
+
23
+ def check_name
24
+ 'socket'
25
+ end
26
+
27
+ def initialize(*args)
28
+ super
29
+ @open_timeout = (open_timeout || 1).to_i
30
+ @read_timeout = (read_timeout || timeout || 5).to_i
31
+
32
+ if addr =~ %r[\Atcp://(.*?):(.*?)\z]
33
+ @socket_family = :tcp
34
+ @socket_addr = $1
35
+ @socket_port = $2.to_i
36
+ elsif addr =~ %r[\Aunix:(.*)\z]
37
+ @socket_family = :unix
38
+ @socket_path = $1
39
+ end
40
+ end
41
+
42
+ def get_value
43
+ Celluloid::Future.new{ get_value_sync }.value
44
+ end
45
+
46
+ def get_value_sync
47
+ sock = Timeout::timeout(@open_timeout) do
48
+ if @socket_family == :tcp
49
+ TCPSocket.open(@socket_addr, @socket_port)
50
+ elsif @socket_family == :unix
51
+ UNIXSocket.open(@socket_path)
52
+ else
53
+ raise "Unknown socket addr #{addr}"
54
+ end
55
+ end
56
+
57
+ if send_data
58
+ Timeout::timeout(@read_timeout) do
59
+ _write_data(sock, send_data)
60
+ { :result => _read_data(sock) }
61
+ end
62
+ else
63
+ { :result => :listen }
64
+ end
65
+
66
+ rescue Timeout::Error
67
+ debug 'Timeout error'
68
+ { :exception => :timeout }
69
+
70
+ rescue Exception => e
71
+ warn "Exception #{e.message}"
72
+ { :exception => e.message }
73
+
74
+ ensure
75
+ sock.close if sock
76
+ end
77
+
78
+ def good?(value)
79
+ return false if !value[:result]
80
+
81
+ if expect_data
82
+ if expect_data.is_a?(Proc)
83
+ match = begin
84
+ !!expect_data[value[:result]]
85
+ rescue Timeout::Error, Exception => ex
86
+ error "proc match failed with #{ex.message}"
87
+ return false
88
+ end
89
+
90
+ warn "proc #{expect_data} not matched (#{value[:result].truncate(30)}) answer" unless match
91
+ return match
92
+ end
93
+
94
+ return true if expect_data.is_a?(Regexp) && expect_data.match(value[:result])
95
+ return true if value[:result].to_s == expect_data.to_s
96
+
97
+ warn "#{expect_data} not matched (#{value[:result].truncate(30)}) answer"
98
+ return false
99
+ end
100
+
101
+ return true
102
+ end
103
+
104
+ def human_value(value)
105
+ if !value.is_a?(Hash)
106
+ '-'
107
+ elsif value[:exception]
108
+ if value[:exception] == :timeout
109
+ 'T-out'
110
+ else
111
+ "Err(#{value[:exception]})"
112
+ end
113
+ else
114
+ if value[:result] == :listen
115
+ "listen"
116
+ else
117
+ "#{value[:result].to_s.size}b"
118
+ end
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def _write_data(socket, data)
125
+ case protocol
126
+ when :em_object
127
+ data = Marshal.dump(data)
128
+ socket.write([data.bytesize, data].pack('Na*'))
129
+ else
130
+ socket.write(data.to_s)
131
+ end
132
+ end
133
+
134
+ def _read_data(socket)
135
+ case protocol
136
+ when :em_object
137
+ content = ""
138
+ msg_size = socket.recv(4).unpack('N')[0] rescue 0
139
+ content << socket.recv(msg_size - content.length) while content.length < msg_size
140
+ if content.present?
141
+ Marshal.load(content) rescue 'corrupted_marshal'
142
+ end
143
+ else
144
+ socket.readline.chop
145
+ end
146
+ end
147
+
148
+ end