zbatery 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +1 -1
- data/bin/zbatery +3 -41
- data/lib/zbatery.rb +21 -1
- data/t/.gitignore +4 -0
- data/t/GNUmakefile +117 -0
- data/t/bin/content-md5-put +36 -0
- data/t/bin/sha1sum.rb +23 -0
- data/t/bin/unused_listen +40 -0
- data/t/bin/utee +12 -0
- data/t/large-file-response.ru +14 -0
- data/t/my-tap-lib.sh +200 -0
- data/t/sleep.ru +13 -0
- data/t/t0003-reopen-logs.sh +107 -0
- data/t/t0005-large-file-response.sh +83 -0
- data/t/test-lib.sh +155 -0
- data/zbatery.gemspec +2 -1
- metadata +25 -3
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
data/bin/zbatery
CHANGED
@@ -112,49 +112,11 @@ end
|
|
112
112
|
config = ARGV[0] || "config.ru"
|
113
113
|
abort "configuration file #{config} not found" unless File.exist?(config)
|
114
114
|
|
115
|
-
|
116
|
-
# parse embedded command-line options in config.ru comments
|
117
|
-
if File.open(config, "rb") { |fp| fp.sysread(fp.stat.size) } =~ /^#\\(.*)/
|
118
|
-
opts.parse! $1.split(/\s+/)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
require 'pp' if $DEBUG
|
123
|
-
|
124
|
-
app = lambda do ||
|
125
|
-
# require Rack as late as possible in case $LOAD_PATH is modified
|
126
|
-
# in config.ru or command-line
|
127
|
-
inner_app = case config
|
128
|
-
when /\.ru$/
|
129
|
-
raw = File.open(config, "rb") { |fp| fp.sysread(fp.stat.size) }
|
130
|
-
raw.sub!(/^__END__\n.*/, '')
|
131
|
-
eval("Rack::Builder.new {(#{raw}\n)}.to_app", nil, config)
|
132
|
-
else
|
133
|
-
require config
|
134
|
-
Object.const_get(File.basename(config, '.rb').capitalize)
|
135
|
-
end
|
136
|
-
pp({ :inner_app => inner_app }) if $DEBUG
|
137
|
-
case ENV["RACK_ENV"]
|
138
|
-
when "development"
|
139
|
-
Rack::Builder.new do
|
140
|
-
use Rack::CommonLogger, $stderr
|
141
|
-
use Rack::ShowExceptions
|
142
|
-
use Rack::Lint
|
143
|
-
run inner_app
|
144
|
-
end.to_app
|
145
|
-
when "deployment"
|
146
|
-
Rack::Builder.new do
|
147
|
-
use Rack::CommonLogger, $stderr
|
148
|
-
run inner_app
|
149
|
-
end.to_app
|
150
|
-
else
|
151
|
-
inner_app
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
115
|
+
app = Unicorn.builder(config, opts)
|
155
116
|
listeners << "#{host}:#{port}" if set_listener
|
156
117
|
|
157
118
|
if $DEBUG
|
119
|
+
require 'pp'
|
158
120
|
pp({
|
159
121
|
:zbatery_options => options,
|
160
122
|
:app => app,
|
@@ -162,5 +124,5 @@ if $DEBUG
|
|
162
124
|
})
|
163
125
|
end
|
164
126
|
|
165
|
-
Unicorn::Launcher.daemonize! if daemonize
|
127
|
+
Unicorn::Launcher.daemonize!(options) if daemonize
|
166
128
|
Zbatery.run(app, options)
|
data/lib/zbatery.rb
CHANGED
@@ -4,7 +4,7 @@ require 'rainbows'
|
|
4
4
|
module Zbatery
|
5
5
|
|
6
6
|
# current version of Zbatery
|
7
|
-
VERSION = "0.
|
7
|
+
VERSION = "0.2.0"
|
8
8
|
|
9
9
|
class << self
|
10
10
|
|
@@ -60,6 +60,7 @@ module Zbatery
|
|
60
60
|
|
61
61
|
# no-op
|
62
62
|
def maintain_worker_count; end
|
63
|
+
def init_self_pipe!; end
|
63
64
|
|
64
65
|
# can't just do a graceful exit if reopening logs fails, so we just
|
65
66
|
# continue on...
|
@@ -71,6 +72,10 @@ module Zbatery
|
|
71
72
|
logger.error "failed reopening logs #{e.message}"
|
72
73
|
end
|
73
74
|
|
75
|
+
def trap_deferred(sig)
|
76
|
+
# nothing
|
77
|
+
end
|
78
|
+
|
74
79
|
def join
|
75
80
|
begin
|
76
81
|
trap(:INT) { stop(false) } # Mongrel trapped INT for Win32...
|
@@ -91,6 +96,13 @@ module Zbatery
|
|
91
96
|
rescue => e # hopefully ignores errors on Win32...
|
92
97
|
logger.error "failed to setup signal handler: #{e.message}"
|
93
98
|
end
|
99
|
+
|
100
|
+
if ready_pipe
|
101
|
+
ready_pipe.syswrite($$.to_s)
|
102
|
+
ready_pipe.close rescue nil
|
103
|
+
self.ready_pipe = nil
|
104
|
+
end
|
105
|
+
|
94
106
|
worker = Worker.new(0, DeadIO.new)
|
95
107
|
before_fork.call(self, worker)
|
96
108
|
worker_loop(worker) # runs forever
|
@@ -125,7 +137,15 @@ module Rainbows
|
|
125
137
|
# master == worker in our case
|
126
138
|
def init_worker_process(worker)
|
127
139
|
after_fork.call(self, worker)
|
140
|
+
worker.user(*user) if user.kind_of?(Array) && ! worker.switched
|
128
141
|
build_app! unless preload_app
|
142
|
+
|
143
|
+
# avoid spurious wakeups and blocking-accept() with 1.8 green threads
|
144
|
+
if RUBY_VERSION.to_f < 1.9
|
145
|
+
require "io/nonblock"
|
146
|
+
HttpServer::LISTENERS.each { |l| l.nonblock = true }
|
147
|
+
end
|
148
|
+
|
129
149
|
logger.info "Zbatery #@use worker_connections=#@worker_connections"
|
130
150
|
end
|
131
151
|
end
|
data/t/.gitignore
ADDED
data/t/GNUmakefile
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# we can run tests in parallel with GNU make
|
2
|
+
|
3
|
+
all::
|
4
|
+
|
5
|
+
pid := $(shell echo $$PPID)
|
6
|
+
|
7
|
+
RUBY = ruby
|
8
|
+
zbatery_lib := $(shell cd ../lib && pwd)
|
9
|
+
-include ../local.mk
|
10
|
+
ifeq ($(RUBY_VERSION),)
|
11
|
+
RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
|
12
|
+
endif
|
13
|
+
|
14
|
+
ifeq ($(RUBY_VERSION),)
|
15
|
+
$(error unable to detect RUBY_VERSION)
|
16
|
+
endif
|
17
|
+
|
18
|
+
ifeq ($(RUBYLIB),)
|
19
|
+
RUBYLIB := $(zbatery_lib)
|
20
|
+
else
|
21
|
+
RUBYLIB := $(zbatery_lib):$(RUBYLIB)
|
22
|
+
endif
|
23
|
+
export RUBYLIB RUBY_VERSION
|
24
|
+
|
25
|
+
models += ThreadPool
|
26
|
+
models += ThreadSpawn
|
27
|
+
models += Rev
|
28
|
+
models += EventMachine
|
29
|
+
models += NeverBlock
|
30
|
+
models += RevThreadSpawn
|
31
|
+
models += RevThreadPool
|
32
|
+
|
33
|
+
rp := )
|
34
|
+
ONENINE := $(shell case $(RUBY_VERSION) in 1.9.*$(rp) echo true;;esac)
|
35
|
+
ifeq ($(ONENINE),true)
|
36
|
+
models += Revactor
|
37
|
+
models += FiberSpawn
|
38
|
+
models += RevFiberSpawn
|
39
|
+
models += FiberPool
|
40
|
+
endif
|
41
|
+
all_models := $(models) Base
|
42
|
+
|
43
|
+
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
|
44
|
+
|
45
|
+
MODEL_T := $(foreach m,$(all_models),$(addprefix $(m).,$(T)))
|
46
|
+
$(T): MODELS = $(models)
|
47
|
+
|
48
|
+
# some tests can be run with all models
|
49
|
+
t0000-simple-http.sh: MODELS = $(all_models)
|
50
|
+
t0001-unix-http.sh: MODELS = $(all_models)
|
51
|
+
t0002-graceful.sh: MODELS = $(all_models)
|
52
|
+
t0002-parser-error.sh: MODELS = $(all_models)
|
53
|
+
t0003-reopen-logs.sh: MODELS = $(all_models)
|
54
|
+
|
55
|
+
# recursively run per-model tests
|
56
|
+
# haven't figured out a good way to make make non-recursive here, yet...
|
57
|
+
$(T):
|
58
|
+
$(MAKE) $(foreach m,$(MODELS),$(addprefix $(m).,$@))
|
59
|
+
|
60
|
+
$(all_models):
|
61
|
+
$(MAKE) $(filter $@.%,$(MODEL_T))
|
62
|
+
|
63
|
+
all:: $(T)
|
64
|
+
|
65
|
+
# can't rely on "set -o pipefail" since we don't require bash or ksh93 :<
|
66
|
+
t_pfx = trash/$@-$(RUBY_VERSION)
|
67
|
+
TEST_OPTS =
|
68
|
+
# TRACER = strace -f -o $(t_pfx).strace -s 100000
|
69
|
+
# TRACER = /usr/bin/time -o $(t_pfx).time
|
70
|
+
|
71
|
+
ifdef V
|
72
|
+
ifeq ($(V),2)
|
73
|
+
TEST_OPTS += --trace
|
74
|
+
else
|
75
|
+
TEST_OPTS += --verbose
|
76
|
+
endif
|
77
|
+
endif
|
78
|
+
|
79
|
+
test-bin-$(RUBY_VERSION)/zbatery: ruby_bin = $(shell which $(RUBY))
|
80
|
+
test-bin-$(RUBY_VERSION)/zbatery: ../bin/zbatery
|
81
|
+
mkdir -p $(@D)
|
82
|
+
install -m 755 $^ $@.$(pid)
|
83
|
+
$(RUBY) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@.$(pid)
|
84
|
+
mv $@.$(pid) $@
|
85
|
+
|
86
|
+
random_blob:
|
87
|
+
dd if=/dev/urandom bs=1M count=30 of=$@.$(pid)
|
88
|
+
mv $@.$(pid) $@
|
89
|
+
|
90
|
+
$(T): random_blob
|
91
|
+
|
92
|
+
dependencies := socat curl
|
93
|
+
deps := $(addprefix .dep+,$(dependencies))
|
94
|
+
$(deps): dep_bin = $(lastword $(subst +, ,$@))
|
95
|
+
$(deps):
|
96
|
+
@which $(dep_bin) > $@.$(pid) 2>/dev/null || :
|
97
|
+
@test -s $@.$(pid) || \
|
98
|
+
{ echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
|
99
|
+
@mv $@.$(pid) $@
|
100
|
+
dep: $(deps)
|
101
|
+
|
102
|
+
$(MODEL_T): export model = $(firstword $(subst ., ,$@))
|
103
|
+
$(MODEL_T): script = $(subst $(model).,,$@)
|
104
|
+
$(MODEL_T): trash/.gitignore
|
105
|
+
$(MODEL_T): export RUBY := $(RUBY)
|
106
|
+
$(MODEL_T): export PATH := $(CURDIR)/test-bin-$(RUBY_VERSION):$(PATH)
|
107
|
+
$(MODEL_T): test-bin-$(RUBY_VERSION)/zbatery dep
|
108
|
+
$(TRACER) $(SHELL) $(SH_TEST_OPTS) $(script) $(TEST_OPTS)
|
109
|
+
|
110
|
+
trash/.gitignore:
|
111
|
+
mkdir -p $(@D)
|
112
|
+
echo '*' > $@
|
113
|
+
|
114
|
+
clean:
|
115
|
+
$(RM) -r trash/*.log trash/*.code test-bin-$(RUBY_VERSION)
|
116
|
+
|
117
|
+
.PHONY: $(T) clean
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: binary -*-
|
3
|
+
# simple chunked HTTP PUT request generator (and just that),
|
4
|
+
# it reads stdin and writes to stdout so socat can write to a
|
5
|
+
# UNIX or TCP socket (or to another filter or file) along with
|
6
|
+
# a Content-MD5 trailer.
|
7
|
+
require 'digest/md5'
|
8
|
+
$stdout.sync = $stderr.sync = true
|
9
|
+
$stdout.binmode
|
10
|
+
$stdin.binmode
|
11
|
+
|
12
|
+
bs = ENV['bs'] ? ENV['bs'].to_i : 4096
|
13
|
+
|
14
|
+
if ARGV.grep("--no-headers").empty?
|
15
|
+
$stdout.write(
|
16
|
+
"PUT / HTTP/1.1\r\n" \
|
17
|
+
"Host: example.com\r\n" \
|
18
|
+
"Transfer-Encoding: chunked\r\n" \
|
19
|
+
"Trailer: Content-MD5\r\n" \
|
20
|
+
"\r\n"
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
digest = Digest::MD5.new
|
25
|
+
if buf = $stdin.readpartial(bs)
|
26
|
+
begin
|
27
|
+
digest.update(buf)
|
28
|
+
$stdout.write("%x\r\n" % [ buf.size ])
|
29
|
+
$stdout.write(buf)
|
30
|
+
$stdout.write("\r\n")
|
31
|
+
end while $stdin.read(bs, buf)
|
32
|
+
end
|
33
|
+
|
34
|
+
digest = [ digest.digest ].pack('m').strip
|
35
|
+
$stdout.write("0\r\n")
|
36
|
+
$stdout.write("Content-MD5: #{digest}\r\n\r\n")
|
data/t/bin/sha1sum.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: binary -*-
|
3
|
+
|
4
|
+
# Reads from stdin and outputs the SHA1 hex digest of the input this is
|
5
|
+
# ONLY used as a last resort, our test code will try to use sha1sum(1),
|
6
|
+
# openssl(1), or gsha1sum(1) before falling back to using this. We try
|
7
|
+
# all options first because we have a strong and healthy distrust of our
|
8
|
+
# Ruby abilities in general, and *especially* when it comes to
|
9
|
+
# understanding (and trusting the implementation of) Ruby 1.9 encoding.
|
10
|
+
|
11
|
+
require 'digest/sha1'
|
12
|
+
$stdout.sync = $stderr.sync = true
|
13
|
+
$stdout.binmode
|
14
|
+
$stdin.binmode
|
15
|
+
bs = 16384
|
16
|
+
digest = Digest::SHA1.new
|
17
|
+
if buf = $stdin.read(bs)
|
18
|
+
begin
|
19
|
+
digest.update(buf)
|
20
|
+
end while $stdin.read(bs, buf)
|
21
|
+
end
|
22
|
+
|
23
|
+
$stdout.syswrite("#{digest.hexdigest}\n")
|
data/t/bin/unused_listen
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: binary -*-
|
3
|
+
# this is to remain compatible with the unused_port function in the
|
4
|
+
# Unicorn test/test_helper.rb file
|
5
|
+
require 'socket'
|
6
|
+
require 'tmpdir'
|
7
|
+
|
8
|
+
default_port = 8080
|
9
|
+
addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
|
10
|
+
retries = 100
|
11
|
+
base = 5000
|
12
|
+
port = sock = lock_path = nil
|
13
|
+
|
14
|
+
begin
|
15
|
+
begin
|
16
|
+
port = base + rand(32768 - base)
|
17
|
+
while port == default_port
|
18
|
+
port = base + rand(32768 - base)
|
19
|
+
end
|
20
|
+
|
21
|
+
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
22
|
+
sock.bind(Socket.pack_sockaddr_in(port, addr))
|
23
|
+
sock.listen(5)
|
24
|
+
rescue Errno::EADDRINUSE, Errno::EACCES
|
25
|
+
sock.close rescue nil
|
26
|
+
retry if (retries -= 1) >= 0
|
27
|
+
end
|
28
|
+
|
29
|
+
# since we'll end up closing the random port we just got, there's a race
|
30
|
+
# condition could allow the random port we just chose to reselect itself
|
31
|
+
# when running tests in parallel with gmake. Create a lock file while
|
32
|
+
# we have the port here to ensure that does not happen.
|
33
|
+
lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
|
34
|
+
lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
|
35
|
+
rescue Errno::EEXIST
|
36
|
+
sock.close rescue nil
|
37
|
+
retry
|
38
|
+
end
|
39
|
+
sock.close rescue nil
|
40
|
+
puts %Q(listen=#{addr}:#{port} T_RM_LIST="$T_RM_LIST #{lock_path}")
|
data/t/bin/utee
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: binary -*-
|
3
|
+
# tee(1) as distributed on most(all?) systems is buffered in luserspace
|
4
|
+
# this only does unbuffered writes (with line-buffered input) to make
|
5
|
+
# test output appear in real-time
|
6
|
+
$stdin.binmode
|
7
|
+
$stdout.binmode
|
8
|
+
fp = File.open(ARGV.shift, "wb")
|
9
|
+
$stdin.each_line do |line|
|
10
|
+
fp.syswrite line
|
11
|
+
$stdout.syswrite line
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# lib-large-file-response will stop running if we're not on Linux here
|
2
|
+
use Rack::ContentLength
|
3
|
+
use Rack::ContentType
|
4
|
+
map "/rss" do
|
5
|
+
run lambda { |env|
|
6
|
+
# on Linux, this is in kilobytes
|
7
|
+
GC.start if GC.respond_to?(:start)
|
8
|
+
::File.read("/proc/self/status") =~ /^VmRSS:\s+(\d+)/
|
9
|
+
[ 200, {}, [ ($1.to_i * 1024).to_s ] ]
|
10
|
+
}
|
11
|
+
end
|
12
|
+
map "/" do
|
13
|
+
run Rack::File.new(Dir.pwd)
|
14
|
+
end
|
data/t/my-tap-lib.sh
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
# Copyright (c) 2009 Eric Wong <normalperson@yhbt.net>
|
3
|
+
#
|
4
|
+
# TAP-producing shell library for POSIX-compliant Bourne shells We do
|
5
|
+
# not _rely_ on Bourne Again features, though we will use "set -o
|
6
|
+
# pipefail" from ksh93 or bash 3 if available
|
7
|
+
#
|
8
|
+
# Only generic, non-project/non-language-specific stuff goes here. We
|
9
|
+
# only have POSIX dependencies for the core tests (without --verbose),
|
10
|
+
# though we'll enable useful non-POSIX things if they're available.
|
11
|
+
#
|
12
|
+
# This test library is intentionally unforgiving, it does not support
|
13
|
+
# skipping tests nor continuing after any failure. Any failures
|
14
|
+
# immediately halt execution as do any references to undefined
|
15
|
+
# variables.
|
16
|
+
#
|
17
|
+
# When --verbose is specified, we always prefix stdout/stderr
|
18
|
+
# output with "#" to avoid confusing TAP consumers. Otherwise
|
19
|
+
# the normal stdout/stderr streams are redirected to /dev/null
|
20
|
+
|
21
|
+
# dup normal stdout(fd=1) and stderr (fd=2) to fd=3 and fd=4 respectively
|
22
|
+
# normal TAP output goes to fd=3, nothing should go to fd=4
|
23
|
+
exec 3>&1 4>&2
|
24
|
+
|
25
|
+
# ensure a sane environment
|
26
|
+
TZ=UTC LC_ALL=C LANG=C
|
27
|
+
export LANG LC_ALL TZ
|
28
|
+
unset CDPATH
|
29
|
+
|
30
|
+
# pipefail is non-POSIX, but very useful in ksh93/bash
|
31
|
+
( set -o pipefail 2>/dev/null ) && set -o pipefail
|
32
|
+
|
33
|
+
SED=${SED-sed}
|
34
|
+
|
35
|
+
# Unlike other test frameworks, we are unforgiving and bail immediately
|
36
|
+
# on any failures. We do this because we're lazy about error handling
|
37
|
+
# and also because we believe anything broken should not be allowed to
|
38
|
+
# propagate throughout the rest of the test
|
39
|
+
set -e
|
40
|
+
set -u
|
41
|
+
|
42
|
+
# name of our test
|
43
|
+
T=${0##*/}
|
44
|
+
|
45
|
+
t_expect_nr=-1
|
46
|
+
t_nr=0
|
47
|
+
t_current=
|
48
|
+
t_complete=false
|
49
|
+
|
50
|
+
# list of files to remove unconditionally on exit
|
51
|
+
T_RM_LIST=
|
52
|
+
|
53
|
+
# list of files to remove only on successful exit
|
54
|
+
T_OK_RM_LIST=
|
55
|
+
|
56
|
+
# emit output to stdout, it'll be parsed by the TAP consumer
|
57
|
+
# so it must be TAP-compliant output
|
58
|
+
t_echo () {
|
59
|
+
echo >&3 "$@"
|
60
|
+
}
|
61
|
+
|
62
|
+
# emits non-parsed information to stdout, it will be prefixed with a '#'
|
63
|
+
# to not throw off TAP consumers
|
64
|
+
t_info () {
|
65
|
+
t_echo '#' "$@"
|
66
|
+
}
|
67
|
+
|
68
|
+
# exit with an error and print a diagnostic
|
69
|
+
die () {
|
70
|
+
echo >&2 "$@"
|
71
|
+
exit 1
|
72
|
+
}
|
73
|
+
|
74
|
+
# our at_exit handler, it'll fire for all exits except SIGKILL (unavoidable)
|
75
|
+
t_at_exit () {
|
76
|
+
code=$?
|
77
|
+
set +e
|
78
|
+
if test $code -eq 0
|
79
|
+
then
|
80
|
+
$t_complete || {
|
81
|
+
t_info "t_done not called"
|
82
|
+
code=1
|
83
|
+
}
|
84
|
+
elif test -n "$t_current"
|
85
|
+
then
|
86
|
+
t_echo "not ok $t_nr - $t_current"
|
87
|
+
fi
|
88
|
+
if test $t_expect_nr -ne -1
|
89
|
+
then
|
90
|
+
test $t_expect_nr -eq $t_nr || {
|
91
|
+
t_info "planned $t_expect_nr tests but ran $t_nr"
|
92
|
+
test $code -ne 0 || code=1
|
93
|
+
}
|
94
|
+
fi
|
95
|
+
$t_complete || {
|
96
|
+
t_info "unexpected test failure"
|
97
|
+
test $code -ne 0 || code=1
|
98
|
+
}
|
99
|
+
rm -f $T_RM_LIST
|
100
|
+
test $code -eq 0 && rm -f $T_OK_RM_LIST
|
101
|
+
set +x
|
102
|
+
exec >&3 2>&4
|
103
|
+
t_close_fds
|
104
|
+
exit $code
|
105
|
+
}
|
106
|
+
|
107
|
+
# close test-specific extra file descriptors
|
108
|
+
t_close_fds () {
|
109
|
+
exec 3>&- 4>&-
|
110
|
+
}
|
111
|
+
|
112
|
+
# call this at the start of your test to specify the number of tests
|
113
|
+
# you plan to run
|
114
|
+
t_plan () {
|
115
|
+
test "$1" -ge 1 || die "must plan at least one test"
|
116
|
+
test $t_expect_nr -eq -1 || die "tried to plan twice in one test"
|
117
|
+
t_expect_nr=$1
|
118
|
+
shift
|
119
|
+
t_echo 1..$t_expect_nr "#" "$@"
|
120
|
+
trap t_at_exit EXIT
|
121
|
+
}
|
122
|
+
|
123
|
+
_t_checkup () {
|
124
|
+
test $t_expect_nr -le 0 && die "no tests planned"
|
125
|
+
test -n "$t_current" && t_echo "ok $t_nr - $t_current"
|
126
|
+
true
|
127
|
+
}
|
128
|
+
|
129
|
+
# finalizes any previously test and starts a new one
|
130
|
+
t_begin () {
|
131
|
+
_t_checkup
|
132
|
+
t_nr=$(( $t_nr + 1 ))
|
133
|
+
t_current="$1"
|
134
|
+
|
135
|
+
# just in case somebody wanted to cheat us:
|
136
|
+
set -e
|
137
|
+
}
|
138
|
+
|
139
|
+
# finalizes the current test without starting a new one
|
140
|
+
t_end () {
|
141
|
+
_t_checkup
|
142
|
+
t_current=
|
143
|
+
}
|
144
|
+
|
145
|
+
# run this to signify the end of your test
|
146
|
+
t_done () {
|
147
|
+
_t_checkup
|
148
|
+
t_current=
|
149
|
+
t_complete=true
|
150
|
+
test $t_expect_nr -eq $t_nr || exit 1
|
151
|
+
exit 0
|
152
|
+
}
|
153
|
+
|
154
|
+
# create and assign named-pipes to variable _names_ passed to this function
|
155
|
+
t_fifos () {
|
156
|
+
for _id in "$@"
|
157
|
+
do
|
158
|
+
_name=$_id
|
159
|
+
_tmp=$(mktemp -t $T.$$.$_id.XXXXXXXX)
|
160
|
+
eval "$_id=$_tmp"
|
161
|
+
rm -f $_tmp
|
162
|
+
mkfifo $_tmp
|
163
|
+
T_RM_LIST="$T_RM_LIST $_tmp"
|
164
|
+
done
|
165
|
+
}
|
166
|
+
|
167
|
+
t_verbose=false t_trace=false
|
168
|
+
|
169
|
+
while test "$#" -ne 0
|
170
|
+
do
|
171
|
+
arg="$1"
|
172
|
+
shift
|
173
|
+
case $arg in
|
174
|
+
-v|--verbose) t_verbose=true ;;
|
175
|
+
--trace) t_trace=true t_verbose=true ;;
|
176
|
+
*) die "Unknown option: $arg" ;;
|
177
|
+
esac
|
178
|
+
done
|
179
|
+
|
180
|
+
# we always only setup stdout, nothing should end up in the "real" stderr
|
181
|
+
if $t_verbose
|
182
|
+
then
|
183
|
+
if test x"$(which mktemp 2>/dev/null)" = x
|
184
|
+
then
|
185
|
+
die "mktemp(1) not available for --verbose"
|
186
|
+
fi
|
187
|
+
t_fifos t_stdout t_stderr
|
188
|
+
|
189
|
+
(
|
190
|
+
# use a subshell so seds are not waitable
|
191
|
+
$SED -e 's/^/#: /' $t_stdout &
|
192
|
+
$SED -e 's/^/#! /' $t_stderr &
|
193
|
+
) &
|
194
|
+
exec > $t_stdout 2> $t_stderr
|
195
|
+
else
|
196
|
+
exec > /dev/null 2> /dev/null
|
197
|
+
fi
|
198
|
+
|
199
|
+
$t_trace && set -x
|
200
|
+
true
|
data/t/sleep.ru
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
use Rack::ContentLength
|
2
|
+
|
3
|
+
run lambda { |env|
|
4
|
+
/\A100-continue\z/i =~ env['HTTP_EXPECT'] and return [ 100, {}, [] ]
|
5
|
+
|
6
|
+
env['rack.input'].read
|
7
|
+
nr = 1
|
8
|
+
env["PATH_INFO"] =~ %r{/([\d\.]+)\z} and nr = $1.to_f
|
9
|
+
|
10
|
+
Rainbows.sleep(nr)
|
11
|
+
|
12
|
+
[ 200, {'Content-Type' => 'text/plain'}, [ "Hello\n" ] ]
|
13
|
+
}
|
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
# don't set nr_client for Rev, only _one_ app running at once :x
|
3
|
+
nr_client=${nr_client-2}
|
4
|
+
. ./test-lib.sh
|
5
|
+
|
6
|
+
t_plan 19 "reopen rotated logs"
|
7
|
+
|
8
|
+
t_begin "setup and startup" && {
|
9
|
+
rtmpfiles curl_out curl_err r_rot
|
10
|
+
zbatery_setup $model
|
11
|
+
zbatery -D sleep.ru -c $unicorn_config
|
12
|
+
zbatery_wait_start
|
13
|
+
}
|
14
|
+
|
15
|
+
t_begin "ensure server is responsive" && {
|
16
|
+
curl -sSf http://$listen/ >/dev/null
|
17
|
+
}
|
18
|
+
|
19
|
+
t_begin "start $nr_client concurrent requests" && {
|
20
|
+
start=$(date +%s)
|
21
|
+
for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
|
22
|
+
do
|
23
|
+
( curl -sSf http://$listen/2 >> $curl_out 2>> $curl_err ) &
|
24
|
+
done
|
25
|
+
}
|
26
|
+
|
27
|
+
t_begin "ensure stderr log is clean" && check_stderr
|
28
|
+
|
29
|
+
t_begin "external log rotation" && {
|
30
|
+
rm -f $r_rot
|
31
|
+
mv $r_err $r_rot
|
32
|
+
}
|
33
|
+
|
34
|
+
t_begin "send reopen log signal (USR1)" && {
|
35
|
+
kill -USR1 $zbatery_pid
|
36
|
+
}
|
37
|
+
|
38
|
+
t_begin "wait for rotated log to reappear" && {
|
39
|
+
nr=60
|
40
|
+
while ! test -f $r_err && test $nr -ge 0
|
41
|
+
do
|
42
|
+
sleep 1
|
43
|
+
nr=$(( $nr - 1 ))
|
44
|
+
done
|
45
|
+
}
|
46
|
+
|
47
|
+
t_begin "wait for worker to reopen logs" && {
|
48
|
+
nr=60
|
49
|
+
re="worker=.* done reopening logs"
|
50
|
+
while ! grep "$re" < $r_err >/dev/null && test $nr -ge 0
|
51
|
+
do
|
52
|
+
sleep 1
|
53
|
+
nr=$(( $nr - 1 ))
|
54
|
+
done
|
55
|
+
}
|
56
|
+
|
57
|
+
dbgcat r_rot
|
58
|
+
dbgcat r_err
|
59
|
+
|
60
|
+
t_begin "wait curl requests to finish" && {
|
61
|
+
wait
|
62
|
+
t_info elapsed=$(( $(date +%s) - $start ))
|
63
|
+
}
|
64
|
+
|
65
|
+
t_begin "ensure no errors from curl" && {
|
66
|
+
test ! -s $curl_err
|
67
|
+
}
|
68
|
+
|
69
|
+
t_begin "curl got $nr_client responses" && {
|
70
|
+
test "$(wc -l < $curl_out)" -eq $nr_client
|
71
|
+
}
|
72
|
+
|
73
|
+
t_begin "all responses were identical" && {
|
74
|
+
nr=$(sort < $curl_out | uniq | wc -l)
|
75
|
+
test "$nr" -eq 1
|
76
|
+
}
|
77
|
+
|
78
|
+
t_begin 'response was "Hello"' && {
|
79
|
+
test x$(sort < $curl_out | uniq) = xHello
|
80
|
+
}
|
81
|
+
|
82
|
+
t_begin "current server stderr is clean" && check_stderr
|
83
|
+
|
84
|
+
t_begin "rotated stderr is clean" && {
|
85
|
+
check_stderr $r_rot
|
86
|
+
}
|
87
|
+
|
88
|
+
t_begin "server is now writing logs to new stderr" && {
|
89
|
+
before_rot=$(wc -c < $r_rot)
|
90
|
+
before_err=$(wc -c < $r_err)
|
91
|
+
curl -sSfv http://$listen/
|
92
|
+
after_rot=$(wc -c < $r_rot)
|
93
|
+
after_err=$(wc -c < $r_err)
|
94
|
+
test $after_rot -eq $before_rot
|
95
|
+
test $after_err -gt $before_err
|
96
|
+
}
|
97
|
+
|
98
|
+
t_begin "stop server" && {
|
99
|
+
kill $zbatery_pid
|
100
|
+
}
|
101
|
+
|
102
|
+
dbgcat r_err
|
103
|
+
|
104
|
+
t_begin "current server stderr is clean" && check_stderr
|
105
|
+
t_begin "rotated stderr is clean" && check_stderr $r_rot
|
106
|
+
|
107
|
+
t_done
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
test -r random_blob || die "random_blob required, run with 'make $0'"
|
4
|
+
|
5
|
+
if ! grep -v ^VmRSS: /proc/self/status >/dev/null 2>&1
|
6
|
+
then
|
7
|
+
t_info "skipping, can't read RSS from /proc/self/status"
|
8
|
+
exit 0
|
9
|
+
fi
|
10
|
+
|
11
|
+
t_plan 10 "large file response slurp avoidance for $model"
|
12
|
+
|
13
|
+
t_begin "setup and startup" && {
|
14
|
+
rtmpfiles curl_out
|
15
|
+
zbatery_setup $model
|
16
|
+
# can't load Rack::Lint here since it'll cause Rev to slurp
|
17
|
+
zbatery -E none -D large-file-response.ru -c $unicorn_config
|
18
|
+
zbatery_wait_start
|
19
|
+
}
|
20
|
+
|
21
|
+
t_begin "read random blob size" && {
|
22
|
+
random_blob_size=$(wc -c < random_blob)
|
23
|
+
}
|
24
|
+
|
25
|
+
t_begin "read current RSS" && {
|
26
|
+
curl -v http://$listen/rss
|
27
|
+
dbgcat r_err
|
28
|
+
rss_before=$(curl -sSfv http://$listen/rss)
|
29
|
+
t_info "rss_before=$rss_before"
|
30
|
+
}
|
31
|
+
|
32
|
+
t_begin "send a series HTTP/1.1 requests sequentially" && {
|
33
|
+
for i in a b c
|
34
|
+
do
|
35
|
+
size=$( (curl -sSfv http://$listen/random_blob &&
|
36
|
+
echo ok >$ok) |wc -c)
|
37
|
+
test $size -eq $random_blob_size
|
38
|
+
test xok = x$(cat $ok)
|
39
|
+
done
|
40
|
+
}
|
41
|
+
|
42
|
+
# this was a problem during development
|
43
|
+
t_begin "HTTP/1.0 test" && {
|
44
|
+
size=$( (curl -0 -sSfv http://$listen/random_blob &&
|
45
|
+
echo ok >$ok) |wc -c)
|
46
|
+
test $size -eq $random_blob_size
|
47
|
+
test xok = x$(cat $ok)
|
48
|
+
}
|
49
|
+
|
50
|
+
t_begin "HTTP/0.9 test" && {
|
51
|
+
(
|
52
|
+
printf 'GET /random_blob\r\n'
|
53
|
+
cat $fifo > $tmp &
|
54
|
+
wait
|
55
|
+
echo ok > $ok
|
56
|
+
) | socat - TCP:$listen > $fifo
|
57
|
+
cmp $tmp random_blob
|
58
|
+
test xok = x$(cat $ok)
|
59
|
+
}
|
60
|
+
|
61
|
+
dbgcat r_err
|
62
|
+
|
63
|
+
t_begin "read RSS again" && {
|
64
|
+
curl -v http://$listen/rss
|
65
|
+
rss_after=$(curl -sSfv http://$listen/rss)
|
66
|
+
t_info "rss_after=$rss_after"
|
67
|
+
}
|
68
|
+
|
69
|
+
t_begin "shutdown server" && {
|
70
|
+
kill -QUIT $zbatery_pid
|
71
|
+
}
|
72
|
+
|
73
|
+
t_begin "compare RSS before and after" && {
|
74
|
+
diff=$(( $rss_after - $rss_before ))
|
75
|
+
t_info "test diff=$diff < orig=$random_blob_size"
|
76
|
+
test $diff -le $random_blob_size
|
77
|
+
}
|
78
|
+
|
79
|
+
dbgcat r_err
|
80
|
+
|
81
|
+
t_begin "check stderr" && check_stderr
|
82
|
+
|
83
|
+
t_done
|
data/t/test-lib.sh
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
# Copyright (c) 2009 Rainbows! developers
|
3
|
+
. ./my-tap-lib.sh
|
4
|
+
|
5
|
+
set +u
|
6
|
+
if test -z "$model"
|
7
|
+
then
|
8
|
+
# defaulting to Base would unfortunately fail some concurrency tests
|
9
|
+
model=ThreadSpawn
|
10
|
+
t_info "model undefined, defaulting to $model"
|
11
|
+
fi
|
12
|
+
|
13
|
+
set -e
|
14
|
+
RUBY="${RUBY-ruby}"
|
15
|
+
RUBY_VERSION=${RUBY_VERSION-$($RUBY -e 'puts RUBY_VERSION')}
|
16
|
+
t_pfx=$PWD/trash/$model.$T-$RUBY_VERSION
|
17
|
+
set -u
|
18
|
+
|
19
|
+
PATH=$PWD/bin:$PATH
|
20
|
+
export PATH
|
21
|
+
|
22
|
+
test -x $PWD/bin/unused_listen || die "must be run in 't' directory"
|
23
|
+
|
24
|
+
wait_for_pid () {
|
25
|
+
path="$1"
|
26
|
+
nr=30
|
27
|
+
while ! test -s "$path" && test $nr -gt 0
|
28
|
+
do
|
29
|
+
nr=$(($nr - 1))
|
30
|
+
sleep 1
|
31
|
+
done
|
32
|
+
}
|
33
|
+
|
34
|
+
# requires $1 and prints out the value of $2
|
35
|
+
require_check () {
|
36
|
+
lib=$1
|
37
|
+
const=$2
|
38
|
+
if ! $RUBY -r$lib -e "puts $const" >/dev/null 2>&1
|
39
|
+
then
|
40
|
+
t_info "skipping $T since we don't have $lib"
|
41
|
+
exit 0
|
42
|
+
fi
|
43
|
+
}
|
44
|
+
|
45
|
+
# given a list of variable names, create temporary files and assign
|
46
|
+
# the pathnames to those variables
|
47
|
+
rtmpfiles () {
|
48
|
+
for id in "$@"
|
49
|
+
do
|
50
|
+
name=$id
|
51
|
+
_tmp=$t_pfx.$id
|
52
|
+
eval "$id=$_tmp"
|
53
|
+
|
54
|
+
case $name in
|
55
|
+
*fifo)
|
56
|
+
rm -f $_tmp
|
57
|
+
mkfifo $_tmp
|
58
|
+
T_RM_LIST="$T_RM_LIST $_tmp"
|
59
|
+
;;
|
60
|
+
*socket)
|
61
|
+
rm -f $_tmp
|
62
|
+
T_RM_LIST="$T_RM_LIST $_tmp"
|
63
|
+
;;
|
64
|
+
*)
|
65
|
+
> $_tmp
|
66
|
+
T_OK_RM_LIST="$T_OK_RM_LIST $_tmp"
|
67
|
+
;;
|
68
|
+
esac
|
69
|
+
done
|
70
|
+
}
|
71
|
+
|
72
|
+
dbgcat () {
|
73
|
+
id=$1
|
74
|
+
eval '_file=$'$id
|
75
|
+
echo "==> $id <=="
|
76
|
+
sed -e "s/^/$id:/" < $_file
|
77
|
+
}
|
78
|
+
|
79
|
+
check_stderr () {
|
80
|
+
set +u
|
81
|
+
_r_err=${1-${r_err}}
|
82
|
+
set -u
|
83
|
+
if grep -i Error $_r_err
|
84
|
+
then
|
85
|
+
die "Errors found in $_r_err"
|
86
|
+
elif grep SIGKILL $_r_err
|
87
|
+
then
|
88
|
+
die "SIGKILL found in $_r_err"
|
89
|
+
fi
|
90
|
+
}
|
91
|
+
|
92
|
+
# zbatery_setup [ MODEL [ WORKER_CONNECTIONS ] ]
|
93
|
+
zbatery_setup () {
|
94
|
+
eval $(unused_listen)
|
95
|
+
rtmpfiles unicorn_config pid r_err r_out fifo tmp ok
|
96
|
+
cat > $unicorn_config <<EOF
|
97
|
+
listen "$listen"
|
98
|
+
pid "$pid"
|
99
|
+
stderr_path "$r_err"
|
100
|
+
stdout_path "$r_out"
|
101
|
+
|
102
|
+
after_fork do |server, worker|
|
103
|
+
# test script will block while reading from $fifo,
|
104
|
+
# so notify the script on the first worker we spawn
|
105
|
+
# by opening the FIFO
|
106
|
+
if worker.nr == 0
|
107
|
+
File.open("$fifo", "wb") { |fp| fp.syswrite "START" }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
EOF
|
111
|
+
{
|
112
|
+
# set a higher default for tests since we run heavily-loaded
|
113
|
+
# boxes and sometimes sleep 1s in tests
|
114
|
+
kato=5
|
115
|
+
echo 'Rainbows! do'
|
116
|
+
if test $# -ge 1
|
117
|
+
then
|
118
|
+
echo " use :$1"
|
119
|
+
test $# -eq 2 && echo " worker_connections $2"
|
120
|
+
if test $# -eq 3
|
121
|
+
then
|
122
|
+
echo " keepalive_timeout $3"
|
123
|
+
else
|
124
|
+
echo " keepalive_timeout $kato"
|
125
|
+
fi
|
126
|
+
else
|
127
|
+
echo " use :$model"
|
128
|
+
echo " keepalive_timeout $kato"
|
129
|
+
fi
|
130
|
+
echo end
|
131
|
+
} >> $unicorn_config
|
132
|
+
}
|
133
|
+
|
134
|
+
zbatery_wait_start () {
|
135
|
+
# "cat $fifo" will block until the before_fork hook is called in
|
136
|
+
# the Unicorn config file
|
137
|
+
test xSTART = x"$(cat $fifo)"
|
138
|
+
zbatery_pid=$(cat $pid)
|
139
|
+
}
|
140
|
+
|
141
|
+
rsha1 () {
|
142
|
+
_cmd="$(which sha1sum 2>/dev/null || :)"
|
143
|
+
test -n "$_cmd" || _cmd="$(which openssl 2>/dev/null || :) sha1"
|
144
|
+
test "$_cmd" != " sha1" || _cmd="$(which gsha1sum 2>/dev/null || :)"
|
145
|
+
|
146
|
+
# last resort, see comments in sha1sum.rb for reasoning
|
147
|
+
test -n "$_cmd" || _cmd=sha1sum.rb
|
148
|
+
expr "$($_cmd < random_blob)" : '\([a-f0-9]\{40\}\)'
|
149
|
+
}
|
150
|
+
|
151
|
+
case $model in
|
152
|
+
Rev) require_check rev Rev::VERSION ;;
|
153
|
+
Revactor) require_check revactor Revactor::VERSION ;;
|
154
|
+
EventMachine) require_check eventmachine EventMachine::VERSION ;;
|
155
|
+
esac
|
data/zbatery.gemspec
CHANGED
@@ -53,7 +53,8 @@ Gem::Specification.new do |s|
|
|
53
53
|
# rainbows 0.90.2 depends on unicorn 0.96.1,
|
54
54
|
# unicorn 0.96.0 and before had a memory leak
|
55
55
|
# that was only triggered in Rainbows!/Zbatery
|
56
|
-
s.add_dependency(%q<
|
56
|
+
s.add_dependency(%q<unicorn>, ["~> 0.97.0"])
|
57
|
+
s.add_dependency(%q<rainbows>, [">= 0.91.0", "<= 1.0.0"])
|
57
58
|
|
58
59
|
# s.licenses = %w(GPLv2 Ruby) # accessor not compatible with older RubyGems
|
59
60
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zbatery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zbatery hackers
|
@@ -9,9 +9,19 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-03-01 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: unicorn
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.97.0
|
24
|
+
version:
|
15
25
|
- !ruby/object:Gem::Dependency
|
16
26
|
name: rainbows
|
17
27
|
type: :runtime
|
@@ -20,7 +30,7 @@ dependencies:
|
|
20
30
|
requirements:
|
21
31
|
- - ">="
|
22
32
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.
|
33
|
+
version: 0.91.0
|
24
34
|
- - <=
|
25
35
|
- !ruby/object:Gem::Version
|
26
36
|
version: 1.0.0
|
@@ -67,6 +77,18 @@ files:
|
|
67
77
|
- local.mk.sample
|
68
78
|
- man/man1/zbatery.1
|
69
79
|
- setup.rb
|
80
|
+
- t/.gitignore
|
81
|
+
- t/GNUmakefile
|
82
|
+
- t/bin/content-md5-put
|
83
|
+
- t/bin/sha1sum.rb
|
84
|
+
- t/bin/unused_listen
|
85
|
+
- t/bin/utee
|
86
|
+
- t/large-file-response.ru
|
87
|
+
- t/my-tap-lib.sh
|
88
|
+
- t/sleep.ru
|
89
|
+
- t/t0003-reopen-logs.sh
|
90
|
+
- t/t0005-large-file-response.sh
|
91
|
+
- t/test-lib.sh
|
70
92
|
- zbatery.gemspec
|
71
93
|
has_rdoc: true
|
72
94
|
homepage: http://zbatery.bogomip.org/
|