uc 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/uc +12 -7
- data/examples/unicorn.rb +44 -0
- data/lib/uc/config.rb +95 -0
- data/lib/uc/custom_logger.rb +24 -0
- data/lib/uc/event.rb +4 -0
- data/lib/uc/event_stream.rb +162 -0
- data/lib/uc/ext/string.rb +29 -0
- data/lib/uc/logger.rb +32 -19
- data/lib/uc/mqueue.rb +1 -1
- data/lib/uc/paths.rb +109 -0
- data/lib/uc/server.rb +60 -71
- data/lib/uc/shell_helper.rb +25 -1
- data/lib/uc/status.rb +49 -0
- data/lib/uc/templates/unicorn.erb +58 -0
- data/lib/uc/unicorn/api.rb +53 -29
- data/lib/uc/unicorn/config.rb +32 -72
- data/lib/uc/unicorn/gradual_shutdown.rb +57 -0
- data/lib/uc/unicorn/helper.rb +25 -0
- data/lib/uc/unicorn/init.rb +58 -0
- data/lib/uc/unicorn/paths.rb +54 -0
- data/lib/uc/unicorn/prestart.rb +35 -69
- data/lib/uc/unicorn/ready_event.rb +58 -0
- data/lib/uc/unicorn/ready_wait.rb +58 -0
- data/lib/uc/version.rb +1 -1
- data/patches/unicorn.patch +45 -0
- metadata +18 -4
- data/lib/uc/mq_logger.rb +0 -22
- data/lib/uc/unicorn/rolling_restart.rb +0 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1136a67ca433cebf902cb4bb1eacc673e61abed
|
4
|
+
data.tar.gz: 77019d47d98b56c17929eff36c90fd7c8aa8a487
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f331fac3d78f17fca9267e1213d225c9b9ed0ba83ee1ea452199605a376fd02174336b4bae6cf45ace8013081f6b5b826b06c3e4ef760bc7fe7a5d9b7116cf6
|
7
|
+
data.tar.gz: 047df981a85aa29710c2ba2cd26e27dcc25b5c0ab716eb1d94a505b29c13801a3d16a50742921b7051a8760f4c863d6fc93b4bf2da76d9aebb02ac1442a4060c
|
data/bin/uc
CHANGED
@@ -42,8 +42,8 @@ opts_parser = OptionParser.new do |opts|
|
|
42
42
|
end
|
43
43
|
|
44
44
|
opts.on("--debug", "Show debug messages") do
|
45
|
+
options[:debug] = true
|
45
46
|
logger.level = ::Logger::DEBUG
|
46
|
-
ENV["UC_DEBUG"] = "true"
|
47
47
|
end
|
48
48
|
|
49
49
|
opts.on_tail("-h", "--help", "Show this message") do
|
@@ -55,13 +55,11 @@ end
|
|
55
55
|
begin
|
56
56
|
opts_parser.parse!(ARGV)
|
57
57
|
app_dir = options[:app_dir] || Dir.pwd
|
58
|
-
|
59
|
-
lock.acquire
|
60
|
-
server = ::Uc::Server.new(app_dir, rails_env: options[:rails_env])
|
58
|
+
server = ::Uc::Server.new(app_dir, rails_env: options[:rails_env], debug: options[:debug])
|
61
59
|
command = ARGV.shift
|
62
60
|
|
63
61
|
case command
|
64
|
-
when "start", "stop", "restart", "status"
|
62
|
+
when "start", "stop", "restart", "status", "print_config"
|
65
63
|
server.send command.to_sym
|
66
64
|
when "rr", "rolling-restart"
|
67
65
|
server.rolling_restart
|
@@ -69,9 +67,16 @@ begin
|
|
69
67
|
logger.error "No command specified"
|
70
68
|
puts opts_parser
|
71
69
|
abort
|
70
|
+
else
|
71
|
+
logger.error "No such command #{command}"
|
72
|
+
abort
|
72
73
|
end
|
73
74
|
|
74
75
|
rescue OptionParser::InvalidOption, OptionParser::MissingArgument, ::Uc::Error => e
|
75
|
-
|
76
|
-
|
76
|
+
if options[:debug]
|
77
|
+
raise e
|
78
|
+
else
|
79
|
+
logger.error "#{e.message}"
|
80
|
+
abort
|
81
|
+
end
|
77
82
|
end
|
data/examples/unicorn.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'uc/unicorn/api'
|
2
|
+
require 'uc/unicorn/config'
|
3
|
+
app_dir = "/path/to/app/dir"
|
4
|
+
|
5
|
+
uc = ::Uc::Unicorn::Api.new
|
6
|
+
uconfig = ::Uc::Unicorn::Config.new(app_dir)
|
7
|
+
|
8
|
+
worker_processes 5
|
9
|
+
working_directory app_dir
|
10
|
+
timeout 30
|
11
|
+
listen uconfig.socket_file, :backlog => 128
|
12
|
+
pid uconfig.pid_file
|
13
|
+
stdout_path uconfig.stdout_log
|
14
|
+
stderr_path uconfig.stderr_log
|
15
|
+
|
16
|
+
preload_app true
|
17
|
+
|
18
|
+
GC.respond_to?(:copy_on_write_friendly=) and
|
19
|
+
GC.copy_on_write_friendly = true
|
20
|
+
|
21
|
+
check_client_connection false
|
22
|
+
|
23
|
+
before_fork do |server, worker|
|
24
|
+
# the following is highly recomended for Rails + "preload_app true"
|
25
|
+
# as there's no need for the master process to hold a connection
|
26
|
+
defined?(ActiveRecord::Base) and
|
27
|
+
ActiveRecord::Base.connection.disconnect!
|
28
|
+
|
29
|
+
if defined?(Resque)
|
30
|
+
Resque.redis.quit
|
31
|
+
Rails.logger.info('Disconnected from Redis')
|
32
|
+
end
|
33
|
+
uc.rolling_restart(server, worker, prestart_wait: 5)
|
34
|
+
end
|
35
|
+
|
36
|
+
after_fork do |server, worker|
|
37
|
+
defined?(ActiveRecord::Base) and
|
38
|
+
ActiveRecord::Base.establish_connection
|
39
|
+
uc.prestart server, worker, url: "/in"
|
40
|
+
end
|
41
|
+
|
42
|
+
before_exec do |server|
|
43
|
+
uc.clean_env
|
44
|
+
end
|
data/lib/uc/config.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'uc/logger'
|
2
|
+
|
3
|
+
module Uc
|
4
|
+
class Config
|
5
|
+
|
6
|
+
include ::Uc::Logger
|
7
|
+
|
8
|
+
def initialize(app_dir, config_file = nil)
|
9
|
+
@config_file = config_file
|
10
|
+
@app_dir = app_dir
|
11
|
+
end
|
12
|
+
|
13
|
+
def config_file
|
14
|
+
@config_file ||= "#{app_dir}/config/uc.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
def config
|
18
|
+
return @config if @config
|
19
|
+
@config = {
|
20
|
+
instances: 2,
|
21
|
+
queue_size: 1024,
|
22
|
+
timeout: 30,
|
23
|
+
prestart_url: "/",
|
24
|
+
working_dir: @app_dir,
|
25
|
+
event_queue: "unicorn_#{Process.uid}",
|
26
|
+
ready_wait: 5
|
27
|
+
}
|
28
|
+
read_from_file
|
29
|
+
return @config
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_h
|
33
|
+
config
|
34
|
+
end
|
35
|
+
|
36
|
+
def ready_wait(wait_timeout)
|
37
|
+
config[:ready_wait] = wait_timeout.to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
def event_queue_name
|
41
|
+
config[:event_queue]
|
42
|
+
end
|
43
|
+
|
44
|
+
def instances(num_instances)
|
45
|
+
config[:instances] = num_instances
|
46
|
+
end
|
47
|
+
|
48
|
+
def backlog(queue_size)
|
49
|
+
config[:queue_size] = queue_size
|
50
|
+
end
|
51
|
+
|
52
|
+
def prestart_url(url)
|
53
|
+
config[:prestart_url] = url
|
54
|
+
end
|
55
|
+
|
56
|
+
def timeout(secs)
|
57
|
+
config[:timeout] = secs
|
58
|
+
end
|
59
|
+
|
60
|
+
def working_dir
|
61
|
+
config[:working_dir] = working_dir
|
62
|
+
end
|
63
|
+
|
64
|
+
def event_queue(event_queue)
|
65
|
+
config[:event_queue] = event_queue
|
66
|
+
end
|
67
|
+
|
68
|
+
def app_dir
|
69
|
+
config[:working_dir]
|
70
|
+
end
|
71
|
+
|
72
|
+
def env_hash
|
73
|
+
@env_hash ||= {}
|
74
|
+
end
|
75
|
+
|
76
|
+
def env(key, value)
|
77
|
+
env_hash[key] = value
|
78
|
+
end
|
79
|
+
|
80
|
+
def load_env
|
81
|
+
config
|
82
|
+
env_hash.each do |k,v|
|
83
|
+
ENV[k] = v
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def read_from_file
|
88
|
+
return if not File.readable? config_file
|
89
|
+
instance_eval(File.read(config_file))
|
90
|
+
rescue NoMethodError => e
|
91
|
+
logger.warn "invalid option used in config: #{e.name}"
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'logger'
|
2
|
+
module Uc
|
3
|
+
class CustomLogger < ::Logger
|
4
|
+
|
5
|
+
def initialize(file)
|
6
|
+
super(file)
|
7
|
+
@level = ::Logger::INFO
|
8
|
+
end
|
9
|
+
|
10
|
+
def format_message(severity, timestamp, progname, msg)
|
11
|
+
case severity
|
12
|
+
when "INFO"
|
13
|
+
"#{msg}\n"
|
14
|
+
when "ERROR"
|
15
|
+
"#{severity.bold.red} #{msg}\n"
|
16
|
+
when "WARN"
|
17
|
+
"#{severity.downcase.bold.yellow} #{msg}\n"
|
18
|
+
else
|
19
|
+
"#{severity.downcase.bold.blue} #{msg}\n"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
data/lib/uc/event.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'uc/ext/string'
|
2
|
+
require 'uc/mqueue'
|
3
|
+
require 'uc/event'
|
4
|
+
|
5
|
+
module Uc
|
6
|
+
class EventStream
|
7
|
+
|
8
|
+
attr_reader :queue_name
|
9
|
+
attr_accessor :debug_output
|
10
|
+
|
11
|
+
def initialize(queue_name)
|
12
|
+
@queue_name = queue_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def info(msg)
|
16
|
+
pub :info, msg
|
17
|
+
end
|
18
|
+
|
19
|
+
def debug(msg)
|
20
|
+
pub :debug, msg
|
21
|
+
end
|
22
|
+
|
23
|
+
def warn(msg)
|
24
|
+
pub :warn, msg
|
25
|
+
end
|
26
|
+
|
27
|
+
def fatal(msg)
|
28
|
+
pub :fatal, msg
|
29
|
+
end
|
30
|
+
|
31
|
+
def pub(type, msg)
|
32
|
+
tmsg = truncate "#{type}|#{msg}"
|
33
|
+
writer.send tmsg
|
34
|
+
rescue Errno::ENOENT, Errno::EAGAIN, Errno::EACCES, Errno::EMSGSIZE => e
|
35
|
+
puts "#{e.class} #{e.message}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def truncate(msg)
|
39
|
+
if msg.size <= mq.msg_size
|
40
|
+
return msg
|
41
|
+
else
|
42
|
+
msg = "#{msg[0, mq.msg_size - 3] }..."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def watch(event_type, timeout: 30, recreate: true, &block)
|
47
|
+
mq.recreate if recreate
|
48
|
+
mq.clear
|
49
|
+
yield
|
50
|
+
wait(event_type, timeout, output: true)
|
51
|
+
rescue => e
|
52
|
+
raise uc_error(e)
|
53
|
+
end
|
54
|
+
|
55
|
+
def watch_in_background(event_type, timeout: 30, recreate: true, &block)
|
56
|
+
begin
|
57
|
+
mq.recreate if recreate
|
58
|
+
mq.clear
|
59
|
+
t = wait_in_background(event_type, timeout, output: true, first_timeout: 50)
|
60
|
+
yield
|
61
|
+
t.join
|
62
|
+
raise t[:error] if t[:error]
|
63
|
+
rescue => e
|
64
|
+
raise uc_error(e)
|
65
|
+
ensure
|
66
|
+
t.kill if t
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def wait(event_type, timeout, output: false, first_timeout: nil)
|
71
|
+
event_type = event_type.to_s
|
72
|
+
message = ""
|
73
|
+
event = ""
|
74
|
+
t = first_timeout || timeout
|
75
|
+
mq.reader do |r|
|
76
|
+
loop do
|
77
|
+
r.receive(message, t)
|
78
|
+
t = timeout
|
79
|
+
event = parse message
|
80
|
+
print event if output
|
81
|
+
break if event.type == event_type
|
82
|
+
end
|
83
|
+
end
|
84
|
+
puts "#{"success".green.bold} #{event.msg}"
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
def wait_in_background(event_type, timeout, **kwargs)
|
89
|
+
Thread.new do
|
90
|
+
begin
|
91
|
+
wait(event_type, timeout, **kwargs)
|
92
|
+
rescue => e
|
93
|
+
Thread.current[:error] = e
|
94
|
+
false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def print(event)
|
100
|
+
case event.type
|
101
|
+
when "info"
|
102
|
+
puts event.msg
|
103
|
+
when "warn"
|
104
|
+
puts "#{"warn".yellow.bold} #{event.msg}"
|
105
|
+
when "debug"
|
106
|
+
puts "#{"debug".blue.bold} #{event.msg}" if debug_output
|
107
|
+
when "fatal"
|
108
|
+
raise ::Uc::Error, event.msg
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def close_connections
|
113
|
+
writer.close if writer
|
114
|
+
@writer = nil
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def read(timeout)
|
121
|
+
event = ""
|
122
|
+
reader do |r|
|
123
|
+
r.receive(event, timeout)
|
124
|
+
parse event
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def parse(event_str)
|
129
|
+
arr = event_str.split("|",2)
|
130
|
+
if arr.length == 2
|
131
|
+
type, msg = arr[0], arr[1]
|
132
|
+
else
|
133
|
+
type, msg = "unknown", event
|
134
|
+
end
|
135
|
+
event = ::Uc::Event.new(type, msg)
|
136
|
+
end
|
137
|
+
|
138
|
+
def mq
|
139
|
+
@mq ||= ::Uc::Mqueue.new(@queue_name)
|
140
|
+
end
|
141
|
+
|
142
|
+
def writer
|
143
|
+
@writer ||= mq.nb_writer
|
144
|
+
end
|
145
|
+
|
146
|
+
def uc_error(e)
|
147
|
+
case e
|
148
|
+
when Errno::EACCES
|
149
|
+
msg = "unable to setup message queue"
|
150
|
+
when Errno::ENOENT
|
151
|
+
msg = "message queue deleted"
|
152
|
+
when Errno::ETIMEDOUT
|
153
|
+
msg = "timeout reached while waiting for server ready msg"
|
154
|
+
else
|
155
|
+
return e
|
156
|
+
end
|
157
|
+
|
158
|
+
return ::Uc::Error.new(msg)
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class String
|
2
|
+
def colorize(color_code)
|
3
|
+
"\e[#{color_code}m#{self}\e[0m"
|
4
|
+
end
|
5
|
+
|
6
|
+
def bold
|
7
|
+
"\e[1m#{self}\e[0m"
|
8
|
+
end
|
9
|
+
|
10
|
+
def white
|
11
|
+
colorize(37)
|
12
|
+
end
|
13
|
+
|
14
|
+
def green
|
15
|
+
colorize(32)
|
16
|
+
end
|
17
|
+
|
18
|
+
def yellow
|
19
|
+
colorize(33)
|
20
|
+
end
|
21
|
+
|
22
|
+
def red
|
23
|
+
colorize(31)
|
24
|
+
end
|
25
|
+
|
26
|
+
def blue
|
27
|
+
colorize(34)
|
28
|
+
end
|
29
|
+
end
|
data/lib/uc/logger.rb
CHANGED
@@ -1,32 +1,45 @@
|
|
1
1
|
require 'logger'
|
2
|
-
require 'uc/
|
2
|
+
require 'uc/custom_logger'
|
3
|
+
require 'uc/event_stream'
|
3
4
|
module Uc
|
4
5
|
module Logger
|
5
|
-
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_reader :event_queue
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.event_queue=(queue_name)
|
12
|
+
@event_stream = nil
|
13
|
+
@event_queue = queue_name
|
14
|
+
end
|
15
|
+
|
6
16
|
def self.logger
|
7
|
-
@logger ||=
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
@logger ||= ::Uc::CustomLogger.new(STDOUT)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.stderr
|
21
|
+
@stderr ||= ::Logger.new(STDERR)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def self.event_stream
|
26
|
+
@event_stream ||= ::Uc::EventStream.new(event_queue)
|
27
|
+
end
|
28
|
+
|
29
|
+
def event_stream
|
30
|
+
::Uc::Logger.event_stream
|
19
31
|
end
|
20
32
|
|
21
33
|
def logger
|
22
34
|
::Uc::Logger.logger
|
23
35
|
end
|
24
36
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
@mq_logger.log msg
|
29
|
-
end
|
37
|
+
def stderr
|
38
|
+
::Uc::Logger.stderr
|
39
|
+
end
|
30
40
|
|
41
|
+
def event_queue
|
42
|
+
::Uc::Logger.event_queue
|
43
|
+
end
|
31
44
|
end
|
32
45
|
end
|
data/lib/uc/mqueue.rb
CHANGED
data/lib/uc/paths.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'uc/logger'
|
3
|
+
|
4
|
+
module Uc
|
5
|
+
class Paths
|
6
|
+
|
7
|
+
include ::Uc::Logger
|
8
|
+
|
9
|
+
attr_reader :app_dir
|
10
|
+
|
11
|
+
def initialize(app_dir)
|
12
|
+
@app_dir = Pathname.new(app_dir )
|
13
|
+
end
|
14
|
+
|
15
|
+
def rack
|
16
|
+
absolute_path "config.ru"
|
17
|
+
end
|
18
|
+
|
19
|
+
def log_dir
|
20
|
+
absolute_path "log"
|
21
|
+
end
|
22
|
+
|
23
|
+
def tmp_dir
|
24
|
+
absolute_path "tmp"
|
25
|
+
end
|
26
|
+
|
27
|
+
def socket_dir
|
28
|
+
absolute_path "tmp/sockets"
|
29
|
+
end
|
30
|
+
|
31
|
+
def pid_dir
|
32
|
+
absolute_path "tmp/pids"
|
33
|
+
end
|
34
|
+
|
35
|
+
def errors
|
36
|
+
@errors ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate_required
|
40
|
+
validate_required_dirs
|
41
|
+
verify_readable rack
|
42
|
+
log_and_raise_errors
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def log_and_raise_errors
|
48
|
+
if not errors.empty?
|
49
|
+
errors.each { |e| logger.error e }
|
50
|
+
raise ::Uc::Error, "exiting due to missing dirs/files"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def required_dirs
|
55
|
+
[ app_dir, tmp_dir, log_dir, socket_dir, pid_dir ]
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate_required_dirs
|
59
|
+
required_dirs.each do |d|
|
60
|
+
validate_dir(d)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def throw_error(type, path)
|
65
|
+
rel_path = rpath(path)
|
66
|
+
case type
|
67
|
+
when :not_exist
|
68
|
+
msg = "path doesn't exist => #{rel_path}"
|
69
|
+
when :not_writable
|
70
|
+
msg = "path not writable => #{rel_path}"
|
71
|
+
when :not_dir
|
72
|
+
msg = "path not a directory => #{rel_path}"
|
73
|
+
end
|
74
|
+
throw :error, msg
|
75
|
+
end
|
76
|
+
|
77
|
+
def validate_dir(dir)
|
78
|
+
relative_path = rpath(dir)
|
79
|
+
error = catch(:error) do
|
80
|
+
throw_error :not_exist, dir if not File.exist? dir
|
81
|
+
throw_error :not_writable, dir if not File.writable? dir
|
82
|
+
throw_error :not_dir, dir if not File.directory? dir
|
83
|
+
end
|
84
|
+
errors << error if error
|
85
|
+
end
|
86
|
+
|
87
|
+
def verify_readable(path)
|
88
|
+
relative_path = rpath(path)
|
89
|
+
if not File.readable? path
|
90
|
+
errors << "path not readable => #{relative_path}"
|
91
|
+
return false
|
92
|
+
else
|
93
|
+
return true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def absolute_path(path)
|
98
|
+
path = Pathname.new(path)
|
99
|
+
raise "absolute path specified: #{path}" if path.absolute?
|
100
|
+
"#{app_dir}/#{path}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def rpath(abs_path)
|
104
|
+
path = Pathname.new abs_path
|
105
|
+
rel_path = path.relative_path_from(app_dir)
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|