eye 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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