uc 0.0.7 → 0.0.8
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.
- 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
|