zbatery 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.1.1.GIT
4
+ DEF_VER=v0.2.0.GIT
5
5
 
6
6
  LF='
7
7
  '
@@ -58,7 +58,7 @@ NEWS: GIT-VERSION-FILE
58
58
  $(RAKE) -s news_rdoc > $@+
59
59
  mv $@+ $@
60
60
 
61
- SINCE = 0.0.0
61
+ SINCE = 0.1.1
62
62
  ChangeLog: LOG_VERSION = \
63
63
  $(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
64
64
  echo $(GIT_VERSION) || git describe)
@@ -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
- if config =~ /\.ru$/
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)
@@ -4,7 +4,7 @@ require 'rainbows'
4
4
  module Zbatery
5
5
 
6
6
  # current version of Zbatery
7
- VERSION = "0.1.1"
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
@@ -0,0 +1,4 @@
1
+ /test-results-*
2
+ /test-bin-*
3
+ /random_blob
4
+ /.dep+*
@@ -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")
@@ -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")
@@ -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}")
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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<rainbows>, [">= 0.90.2", "<= 1.0.0"])
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.1.1
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-02-13 00:00:00 +00:00
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.90.2
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/