unicorn-wrangler 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1caba80d14d2ba78c0141579ee032bc2ac1f19d7
4
+ data.tar.gz: b3ee0aa920698aa8225e2d5795e71240b64490b4
5
+ SHA512:
6
+ metadata.gz: 06a6ac39aa156fc2dded7cfd895fff7870a6ecdc98dbf8170df767c455a735e6f654b60ec5796e2302fabc9b8420047a90935578d6b2be711b1741b84c14d50e
7
+ data.tar.gz: d60b7bb9fdb697cf39d778deeb9749ce78e61c84f4982db231354a975680f528531a126e81791937e4af934852cfe829f884176b2c2a623e6c551ce4660a420a
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in unicorn-wrangler.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Tom Ward
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ # Unicorn::Wrangler
2
+
3
+ A simple process to launch and relaunch unicorn. When launched, it either starts a new unicorn if none is running (found using the given pidfile), or restarts the existing unicorn if one is found.
4
+
5
+ Usage:
6
+
7
+ unicorn-wrangler --pidfile /path/to/unicorn.pid --startup-time 60 -- /path/to/unicorn <unicorn-options>
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'unicorn/wrangler'
4
+ require 'optparse'
5
+
6
+ options = {}
7
+
8
+ command_index = ARGV.index("--")
9
+ command = ARGV[command_index + 1..-1].join(" ")
10
+
11
+ OptionParser.new do |opts|
12
+ opts.on("-p", "--pidfile PIDFILE", "Path to the main unicorn PIDFILE") do |pidfile|
13
+ options[:pidfile] = pidfile
14
+ end
15
+
16
+ opts.on("-s", "--startup-time STARTUP", "Time to wait for server to start") do |time|
17
+ options[:startup] = time.to_i
18
+ end
19
+ end.parse!(ARGV[0...command_index])
20
+
21
+ launcher = Unicorn::Wrangler.new command, options
22
+ launcher.start
@@ -0,0 +1,63 @@
1
+ require "unicorn/wrangler/version"
2
+
3
+ module Unicorn
4
+ class Wrangler
5
+ attr_reader :command, :pidfile, :startup_period, :tick_period, :verbose
6
+
7
+ def initialize(command, options = {})
8
+ @command = command
9
+ @pidfile = File.expand_path(options[:pidfile] || 'unicorn.pid')
10
+ @startup_period = options[:startup] || 60
11
+ end
12
+
13
+ def start
14
+ trap_signals(:QUIT, :INT, :TERM) { exit }
15
+
16
+ if unicorn_running?
17
+ restart_unicorn
18
+ else
19
+ Process.spawn(command)
20
+ wait_for { unicorn_running? }
21
+ end
22
+
23
+ loop do
24
+ exit unless unicorn_running?
25
+ sleep 1
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def trap_signals(*signals, &block)
32
+ signals.map(&:to_s).each do |signal|
33
+ trap(signal) do
34
+ block.call signal
35
+ end
36
+ end
37
+ end
38
+
39
+ def restart_unicorn
40
+ original_pid = unicorn_pid
41
+ Process.kill "USR2", unicorn_pid
42
+ wait_for { unicorn_pid != original_pid }
43
+ sleep startup_period
44
+ Process.kill "TERM", original_pid
45
+ end
46
+
47
+ def unicorn_pid
48
+ File.exist?(pidfile) && File.read(pidfile).to_i
49
+ end
50
+
51
+ def unicorn_running?
52
+ unicorn_pid && Process.getpgid(unicorn_pid)
53
+ rescue Errno::ESRCH
54
+ false
55
+ end
56
+
57
+ def wait_for(&block)
58
+ until block.call
59
+ sleep 0.1
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,5 @@
1
+ module Unicorn
2
+ class Wrangler
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,129 @@
1
+ require 'curl'
2
+
3
+ describe Unicorn::Wrangler do
4
+ def wrangler_path
5
+ Bundler.bin_path + 'unicorn-wrangler'
6
+ end
7
+
8
+ def unicorn_path
9
+ Bundler.bin_path + 'unicorn'
10
+ end
11
+
12
+ def unicorn_cmd
13
+ "#{unicorn_path} -p 7516 -c spec/support/unicorn.conf.rb spec/support/config.ru"
14
+ end
15
+
16
+ def start_unicorn
17
+ Process.spawn unicorn_cmd
18
+ sleep 1
19
+ end
20
+
21
+ def start_wrangler
22
+ @wrangler_pid = Process.spawn "#{wrangler_path} --startup-time 1 -p spec/support/unicorn.pid -- #{unicorn_cmd}"
23
+ sleep 1
24
+ end
25
+
26
+ def unicorn_pid
27
+ File.exist?('spec/support/unicorn.pid') && File.read('spec/support/unicorn.pid').to_i
28
+ end
29
+
30
+ def unicorn_running?
31
+ unicorn_pid && Process.getpgid(unicorn_pid)
32
+ rescue Errno::ESRCH
33
+ false
34
+ end
35
+
36
+ def wrangler_running?
37
+ @wrangler_pid && Process.getpgid(@wrangler_pid)
38
+ rescue Errno::ESRCH
39
+ false
40
+ end
41
+
42
+ def cleanup_processes
43
+ `kill -TERM #{unicorn_pid}` if unicorn_pid
44
+ `kill -TERM #{@wrangler_pid}` if @wrangler_pid
45
+ sleep 1
46
+ end
47
+
48
+ def perform_request
49
+ Curl::Easy.perform('http://localhost:7516').body_str
50
+ end
51
+
52
+ context 'in general' do
53
+ before :each do
54
+ start_wrangler
55
+ end
56
+
57
+ it 'quits (but leaves unicorn running) on receiving QUIT signal' do
58
+ Process.kill 'QUIT', @wrangler_pid
59
+ sleep 1
60
+ wrangler_running?.should_not be_true
61
+ unicorn_running?.should be_true
62
+ end
63
+
64
+ it 'quits (but leaves unicorn running) on receiving TERM signal' do
65
+ Process.kill 'TERM', @wrangler_pid
66
+ sleep 1
67
+ wrangler_running?.should_not be_true
68
+ unicorn_running?.should be_true
69
+ end
70
+
71
+ it 'quits (but leaves unicorn running) on receiving INT signal' do
72
+ Process.kill 'INT', @wrangler_pid
73
+ sleep 1
74
+ wrangler_running?.should_not be_true
75
+ unicorn_running?.should be_true
76
+ end
77
+
78
+ it 'quits if monitored unicorn process quits' do
79
+ Process.kill 'TERM', unicorn_pid
80
+ sleep 3
81
+ wrangler_running?.should_not be_true
82
+ end
83
+
84
+ it 'continues running after launch (so that process monitoring tools do not restart it)' do
85
+ sleep 3
86
+ wrangler_running?.should be_true
87
+ end
88
+ end
89
+
90
+ context 'when unicorn is not running' do
91
+ it 'launches unicorn on startup' do
92
+ start_wrangler
93
+ unicorn_running?.should be_true
94
+ end
95
+ end
96
+
97
+ context 'when unicorn is already running' do
98
+ before :each do
99
+ start_unicorn
100
+ end
101
+
102
+ it 'restarts unicorn on startup' do
103
+ original_pid = unicorn_pid
104
+ start_wrangler
105
+ sleep 3
106
+ unicorn_pid.should_not eql(original_pid)
107
+ end
108
+
109
+ it 'continues serving requests from both unicorns startup period' do
110
+ start_wrangler
111
+ 100.times.map { perform_request }.uniq.size.should eql(2)
112
+ end
113
+
114
+ it 'stops serving requests from original unicorn' do
115
+ original_response = perform_request
116
+ start_wrangler
117
+ sleep 3
118
+ perform_request.should_not eql(original_response)
119
+ end
120
+ end
121
+
122
+ before :each do
123
+ cleanup_processes
124
+ end
125
+
126
+ after :each do
127
+ cleanup_processes
128
+ end
129
+ end
@@ -0,0 +1,4 @@
1
+ # A very simple server that always responds with the time it was started. This
2
+ # is used in tests to check whether the server has started or restarted
3
+ time = Time.now.to_f
4
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, ["#{time}"]] }
@@ -0,0 +1 @@
1
+ pid File.expand_path("../unicorn.pid", __FILE__)
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'unicorn/wrangler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "unicorn-wrangler"
8
+ spec.version = Unicorn::Wrangler::VERSION
9
+ spec.authors = ["Tom Ward"]
10
+ spec.email = ["tom@popdog.net"]
11
+ spec.description = %q{Wrangles unicorns}
12
+ spec.summary = %q{Helps manage unicorn processes}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "unicorn"
24
+ spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "curb"
26
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unicorn-wrangler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tom Ward
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: unicorn
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: curb
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Wrangles unicorns
84
+ email:
85
+ - tom@popdog.net
86
+ executables:
87
+ - unicorn-wrangler
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/unicorn-wrangler
97
+ - lib/unicorn/wrangler.rb
98
+ - lib/unicorn/wrangler/version.rb
99
+ - spec/lib/unicorn/wrangler_spec.rb
100
+ - spec/support/config.ru
101
+ - spec/support/unicorn.conf.rb
102
+ - unicorn-wrangler.gemspec
103
+ homepage: ''
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.0.2
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Helps manage unicorn processes
127
+ test_files:
128
+ - spec/lib/unicorn/wrangler_spec.rb
129
+ - spec/support/config.ru
130
+ - spec/support/unicorn.conf.rb