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,62 @@
1
+ class Eye::Process::StatesHistory < Eye::Utils::Tail
2
+
3
+ def push(state, reason = nil, tm = Time.now)
4
+ super(state: state, at: tm, reason: reason)
5
+ end
6
+
7
+ def states
8
+ self.map{|c| c[:state] }
9
+ end
10
+
11
+ def states_for_period(period)
12
+ tm = Time.now - period
13
+ self.select do |s|
14
+ s[:at] >= tm
15
+ end.map{|c| c[:state] }
16
+ end
17
+
18
+ def last_state
19
+ last[:state]
20
+ end
21
+
22
+ def last_state_changed_at
23
+ last[:at]
24
+ end
25
+
26
+ def seq?(*seq)
27
+ str = states * ','
28
+ substr = seq.flatten * ','
29
+ str.include?(substr)
30
+ end
31
+
32
+ def end?(*seq)
33
+ str = states * ','
34
+ substr = seq.flatten * ','
35
+ str.end_with?(substr)
36
+ end
37
+
38
+ def any?(*seq)
39
+ states.any? do |st|
40
+ seq.flatten.include?(st)
41
+ end
42
+ end
43
+
44
+ def noone?(*seq)
45
+ !states.all? do |st|
46
+ seq.flatten.include?(st)
47
+ end
48
+ end
49
+
50
+ def all?(*seq)
51
+ states.all? do |st|
52
+ seq.flatten.include?(st)
53
+ end
54
+ end
55
+
56
+ def state_count(state)
57
+ states.count do |st|
58
+ st == state
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,60 @@
1
+ require 'timeout'
2
+
3
+ module Eye::Process::System
4
+
5
+ def load_pid_from_file
6
+ if File.exists?(self[:pid_file_ex])
7
+ _pid = File.read(self[:pid_file_ex]).to_i
8
+ _pid > 0 ? _pid : nil
9
+ end
10
+ end
11
+
12
+ def set_pid_from_file
13
+ self.pid = load_pid_from_file
14
+ end
15
+
16
+ def save_pid_to_file
17
+ if self.pid
18
+ File.open(self[:pid_file_ex], 'w') do |f|
19
+ f.write self.pid
20
+ end
21
+ end
22
+
23
+ true
24
+ end
25
+
26
+ def clear_pid_file
27
+ File.unlink(self[:pid_file_ex])
28
+ true
29
+ rescue
30
+ nil
31
+ end
32
+
33
+ def pid_file_ctime
34
+ File.ctime(self[:pid_file_ex]) rescue Time.now
35
+ end
36
+
37
+ def process_realy_running?
38
+ Eye::System.pid_alive?(self.pid) if self.pid
39
+ end
40
+
41
+ def send_signal(code)
42
+ debug "send signal #{code}"
43
+
44
+ res = Eye::System.send_signal(self.pid, code)
45
+ error(res[:message]) if res[:status] != :ok
46
+
47
+ res[:status] == :ok
48
+ end
49
+
50
+ def with_timeout(time, &block)
51
+ Timeout.timeout(time.to_f, &block)
52
+ rescue Timeout::Error
53
+ :timeout
54
+ end
55
+
56
+ def execute(cmd, cfg = {})
57
+ defer{ Eye::System::execute cmd, cfg }
58
+ end
59
+
60
+ end
@@ -0,0 +1,32 @@
1
+ module Eye::Process::Trigger
2
+
3
+ def add_triggers
4
+ if self[:triggers]
5
+ self[:triggers].each do |type, cfg|
6
+ add_trigger(cfg)
7
+ end
8
+ end
9
+ end
10
+
11
+ def remove_triggers
12
+ self.triggers = []
13
+ end
14
+
15
+ def check_triggers
16
+ return if unmonitored?
17
+
18
+ self.triggers.each do |trigger|
19
+ if !trigger.check(self.states_history)
20
+ notify :crit, 'flapping!'
21
+ @flapping = true
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def add_trigger(cfg = {})
29
+ self.triggers << Eye::Trigger.create(cfg, logger.prefix)
30
+ end
31
+
32
+ end
@@ -0,0 +1,67 @@
1
+ module Eye::Process::Watchers
2
+
3
+ def add_watchers(force = false)
4
+ return unless self.up?
5
+
6
+ remove_watchers if force
7
+
8
+ if @watchers.blank?
9
+ # default watcher :check_alive
10
+ add_watcher(:check_alive, self[:check_alive_period]) do
11
+ check_alive
12
+ GC.start if rand(1000) == 0
13
+ end
14
+
15
+ # monitor childs pids
16
+ if self[:monitor_children]
17
+ add_watcher(:check_childs, self[:childs_update_period]) do
18
+ add_or_update_childs
19
+ end
20
+ end
21
+
22
+ # monitor conditional watchers
23
+ start_checkers
24
+ else
25
+ warn 'try add_watchers, but its already here'
26
+ end
27
+ end
28
+
29
+ def remove_watchers
30
+ @watchers.each{|_, h| h[:timer].cancel }
31
+ @watchers = {}
32
+ end
33
+
34
+ private
35
+
36
+ def add_watcher(type, period = 2, subject = nil, &block)
37
+ return if @watchers[type]
38
+
39
+ debug "add watcher #{type}(#{period})"
40
+
41
+ timer = every(period.to_f) do
42
+ debug "check #{type}"
43
+ block.call(subject)
44
+ end
45
+
46
+ @watchers[type] ||= {:timer => timer, :subject => subject}
47
+ end
48
+
49
+ def start_checkers
50
+ self[:checks].each{|type, cfg| start_checker(cfg) }
51
+ end
52
+
53
+ def start_checker(cfg)
54
+ subject = Eye::Checker.create(pid, cfg, logger.prefix)
55
+
56
+ # ex: {:type => :memory, :every => 5.seconds, :below => 100.megabytes, :times => [3,5]}
57
+ add_watcher("check_#{cfg[:type]}".to_sym, subject.every, subject, &method(:watcher_tick).to_proc)
58
+ end
59
+
60
+ def watcher_tick(subject)
61
+ unless subject.check
62
+ notify :crit, "Bounded #{subject.check_name}: #{subject.last_human_values}"
63
+ schedule :restart, "bounded #{subject.check_name}"
64
+ end
65
+ end
66
+
67
+ end
@@ -0,0 +1,51 @@
1
+ require 'celluloid/io'
2
+ require_relative 'io/unix_socket'
3
+ require_relative 'io/unix_server'
4
+
5
+ class Eye::Server
6
+ include Celluloid::IO
7
+
8
+ attr_reader :socket_path, :server
9
+
10
+ def initialize(socket_path)
11
+ @socket_path = socket_path
12
+ @server = begin
13
+ UNIXServer.open(socket_path)
14
+ rescue Errno::EADDRINUSE
15
+ unlink_socket_file
16
+ UNIXServer.open(socket_path)
17
+ end
18
+ end
19
+
20
+ def finalize
21
+ @server.close if @server
22
+ unlink_socket_file
23
+ end
24
+
25
+ def run
26
+ loop { handle_connection! @server.accept }
27
+ end
28
+
29
+ def handle_connection(socket)
30
+ command, *args = socket.readline.strip.split('|')
31
+ response = command(command, *args)
32
+ socket.write(Marshal.dump(response))
33
+
34
+ rescue Errno::EPIPE
35
+ # client timeouted
36
+ # do nothing
37
+
38
+ ensure
39
+ socket.close
40
+ end
41
+
42
+ def command(cmd, *args)
43
+ Eye::Control.command(cmd, *args)
44
+ end
45
+
46
+ def unlink_socket_file
47
+ File.delete(@socket_path) if @socket_path
48
+ rescue
49
+ end
50
+
51
+ end
@@ -0,0 +1,35 @@
1
+ require 'fileutils'
2
+
3
+ module Eye::Settings
4
+
5
+ module_function
6
+
7
+ def dir
8
+ if Process::UID.eid == 0 # root
9
+ '/var/run/eye'
10
+ else
11
+ File.expand_path(File.join(ENV['HOME'], '.eye'))
12
+ end
13
+ end
14
+
15
+ def path(path)
16
+ File.join(dir, path)
17
+ end
18
+
19
+ def ensure_eye_dir
20
+ FileUtils.mkdir_p( dir )
21
+ end
22
+
23
+ def socket_path
24
+ path('sock')
25
+ end
26
+
27
+ def pid_path
28
+ path('pid')
29
+ end
30
+
31
+ def client_timeout
32
+ 5
33
+ end
34
+
35
+ end
@@ -0,0 +1,145 @@
1
+ require 'shellwords'
2
+ require 'pathname'
3
+
4
+ module Eye::System
5
+ class << self
6
+
7
+ # Check that pid realy exits
8
+ # very fast
9
+ def pid_alive?(pid)
10
+ pid ? ::Process.kill(0, pid) && true : false
11
+ rescue Errno::ESRCH, Errno::EPERM
12
+ false
13
+ end
14
+
15
+ # Send signal to process (uses for kill)
16
+ # code: TERM(15), KILL(9), QUIT(3), ...
17
+ def send_signal(pid, code = :TERM)
18
+ code = 0 if code == '0'
19
+ if code.to_s.to_i != 0
20
+ code = code.to_i
21
+ code = -code if code < 0
22
+ end
23
+ code = code.to_s.upcase if code.is_a?(String) || code.is_a?(Symbol)
24
+
25
+ ::Process.kill(code, pid) if pid
26
+ {:status => :ok}
27
+
28
+ rescue Errno::ESRCH
29
+ {:status => :error, :message => 'process not found'}
30
+
31
+ rescue Errno::EPERM
32
+ {:status => :error, :message => 'wrong permissions to kill'}
33
+
34
+ rescue => e
35
+ {:status => :error, :message => "failed signal #{code}: #{e.message}"}
36
+ end
37
+
38
+ # Daemonize cmd, and detach
39
+ # options:
40
+ # :pid_file
41
+ # :working_dir
42
+ # :environment
43
+ # :stdin, :stdout, :stderr
44
+ def daemonize(cmd, cfg = {})
45
+ opts = spawn_options(cfg)
46
+ pid = Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), opts)
47
+ Process.detach(pid)
48
+ {:pid => pid}
49
+
50
+ rescue Errno::ENOENT, Errno::EACCES => ex
51
+ {:error => ex}
52
+ end
53
+
54
+ # Execute cmd with blocking, return status (be careful: inside actor blocks it mailbox, use with defer)
55
+ # options
56
+ # :working_dir
57
+ # :environment
58
+ # :stdin, :stdout, :stderr
59
+ def execute(cmd, cfg = {})
60
+ opts = spawn_options(cfg)
61
+ pid = Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), opts)
62
+
63
+ Timeout.timeout(cfg[:timeout] || 1.second) do
64
+ Process.waitpid(pid)
65
+ end
66
+
67
+ {:pid => pid}
68
+
69
+ rescue Timeout::Error => ex
70
+ send_signal(pid, 9)
71
+ {:error => ex}
72
+
73
+ rescue Errno::ENOENT, Errno::EACCES => ex
74
+ {:error => ex}
75
+ end
76
+
77
+ # get table
78
+ # {pid => {:rss =>, :cpu =>, :ppid => , :cmd => , :start_time}}
79
+ # slow
80
+ def ps_aux
81
+ cmd = if RUBY_PLATFORM.include?('darwin')
82
+ 'ps axo pid,ppid,pcpu,rss,start,command'
83
+ else
84
+ 'ps axo pid,ppid,pcpu,rss,start_time,command'
85
+ end
86
+
87
+ str = Process.send('`', cmd).force_encoding('binary')
88
+ lines = str.split("\n")
89
+ lines.shift # remove first line
90
+ lines.inject(Hash.new) do |mem, line|
91
+ chunk = line.strip.split(/\s+/).map(&:strip)
92
+ mem[chunk[0].to_i] = {
93
+ :rss => chunk[3].to_i,
94
+ :cpu => chunk[2].to_i,
95
+ :ppid => chunk[1].to_i,
96
+ :start_time => chunk[4],
97
+ :cmd => chunk[5..-1].join(' ')
98
+ }
99
+ mem
100
+ end
101
+ end
102
+
103
+ # normalize file
104
+ def normalized_file(file, working_dir = nil)
105
+ Pathname.new(file).expand_path(working_dir).to_s
106
+ end
107
+
108
+ def host
109
+ @host ||= `hostname`.chomp
110
+ end
111
+
112
+ # set host for tests
113
+ def host=(hostname)
114
+ @host = hostname
115
+ end
116
+
117
+ private
118
+
119
+ def spawn_options(config = {})
120
+ o = {}
121
+ o = {chdir: config[:working_dir]} if config[:working_dir]
122
+ o.update(out: [config[:stdout], 'a']) if config[:stdout]
123
+ o.update(err: [config[:stderr], 'a']) if config[:stderr]
124
+ o.update(in: config[:stdin]) if config[:stdin]
125
+ o
126
+ end
127
+
128
+ def prepare_env(config = {})
129
+ env = {}
130
+
131
+ (config[:environment] || {}).each do |k,v|
132
+ env[k.to_s] = v.to_s if v
133
+ end
134
+
135
+ # return original LANG env, because ruby loose it (needs for unicorn)
136
+ env['LANG'] = ENV_LANG unless env['LANG']
137
+
138
+ # set PWD for unicorn respawn
139
+ env['PWD'] = config[:working_dir] if config[:working_dir]
140
+
141
+ env
142
+ end
143
+ end
144
+
145
+ end