zbatery 0.1.1 → 0.2.0

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.
@@ -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/