rack-cluster 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,56 @@
1
+ # Rack::Cluster
2
+
3
+ Tools for managing multiple rackup processes. Built in the image of mongrel_cluster. The real use case is a work around for this [mongrel_rails/Rails >=2.3.3 issue](https://rails.lighthouseapp.com/projects/8994/tickets/2844-bad-content-type-error-in-rack-10-with-first-cgi-mongrel-request).
4
+
5
+ ## Install
6
+
7
+ gem install rack-cluster -s http://gemcutter.com
8
+
9
+ ## Usage
10
+
11
+ rack-cluster [options] command
12
+ -C, --config PATH
13
+ -p, --port PORT use PORT
14
+ -E, --env ENVIRONMENT use ENVIRONMENT for defaults
15
+ -N, --num-servers INT
16
+ -s, --server SERVER serve using SERVER (webrick/mongrel)
17
+ -P, --pid FILE file to store PID
18
+ -c, --chdir PATH
19
+ -u, --config-ru PATH
20
+ -b, --rackup-bin PATH
21
+ -v, --verbose
22
+ -h, --help Show this message
23
+ --version Show version
24
+
25
+ Commands: start, stop, restart
26
+
27
+ ## Caveats
28
+
29
+ * Truly prototype code, but I wanted to get it out there.
30
+
31
+ ## TODO
32
+
33
+ * TEST
34
+ * Make `configure` command akin to mongrel_rails cluster::configure
35
+
36
+ ## License
37
+
38
+ Copyright (c) 2009 Ryan Carmelo Briones &lt;<ryan.briones@brionesandco.com>&gt;
39
+
40
+ Permission is hereby granted, free of charge, to any person obtaining a copy
41
+ of this software and associated documentation files (the "Software"), to deal
42
+ in the Software without restriction, including without limitation the rights
43
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
44
+ copies of the Software, and to permit persons to whom the Software is
45
+ furnished to do so, subject to the following conditions:
46
+
47
+ The above copyright notice and this permission notice shall be included in
48
+ all copies or substantial portions of the Software.
49
+
50
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
56
+ THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ require 'rake/gempackagetask'
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/lib/rack/cluster')
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = 'rack-cluster'
7
+ s.version = Rack::Cluster::VERSION
8
+ s.homepage = 'http://github.com/ryanbriones/rack-cluster'
9
+ s.summary = 'tools for managing multiple rackup processes'
10
+ s.files = FileList['[A-Z]*', 'bin/*', 'lib/**/*']
11
+ s.has_rdoc = false
12
+ s.bindir = 'bin'
13
+ s.executables = ['rack-cluster']
14
+ s.author = 'Ryan Carmelo Briones'
15
+ s.email = 'ryan.briones@brionesandco.com'
16
+
17
+ s.add_runtime_dependency 'rack', '>=1.0.0'
18
+ end
19
+
20
+ Rake::GemPackageTask.new(spec) {}
21
+
22
+ desc "Write out #{spec.name}.gemspec"
23
+ task :build_gemspec do
24
+ File.open("#{spec.name}.gemspec", "w") do |f|
25
+ f.write spec.to_ruby
26
+ end
27
+ end
28
+
29
+ task :default => [:build_gemspec, :gem]
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rack/cluster/command'
3
+
4
+ command = Rack::Cluster::Command.new
5
+ command.parse_options
6
+ command.run
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ module Cluster
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,87 @@
1
+ require 'optparse'
2
+ require 'yaml'
3
+
4
+ require 'rack/cluster'
5
+ require 'rack/cluster/runner'
6
+
7
+ module Rack
8
+ module Cluster
9
+ class Command
10
+ def parse_options
11
+ @options = {}
12
+
13
+ OptionParser.new do |opts|
14
+ opts.banner = "Usage: #{$0} [options] command"
15
+
16
+ opts.on('-C', '--config PATH') do |config|
17
+ @options[:config_file] = config
18
+ end
19
+
20
+ opts.on("-p", "--port PORT", "use PORT") do |p|
21
+ @options[:port] = p
22
+ end
23
+
24
+ opts.on('-E', '--env ENVIRONMENT', 'use ENVIRONMENT for defaults') do |e|
25
+ @options[:environment] = e
26
+ end
27
+
28
+ opts.on('-N', '--num-servers INT') do |n|
29
+ @options[:servers] = n
30
+ end
31
+
32
+ opts.on('-s', '--server SERVER', 'serve using SERVER (webrick/mongrel)') do |s|
33
+ @options[:server] = s
34
+ end
35
+
36
+ opts.on('-P', '--pid FILE', 'file to store PID') do |p|
37
+ @options[:pid_file] = p
38
+ end
39
+
40
+ opts.on('-c', '--chdir PATH') do |c|
41
+ @options[:cwd] = c
42
+ end
43
+
44
+ opts.on('-u', '--config-ru PATH') do |config_ru|
45
+ @options[:config_ru] = config_ru
46
+ end
47
+
48
+ opts.on('-b', '--rackup-bin PATH') do |rackup|
49
+ @options[:rackup] = rackup
50
+ end
51
+
52
+ opts.on('-v', '--verbose') do |verbose|
53
+ @options[:verbose] = verbose
54
+ end
55
+
56
+ opts.on_tail('-h', '--help', 'Show this message') do
57
+ puts opts
58
+ exit
59
+ end
60
+
61
+ opts.on_tail('--version', 'Show version') do
62
+ puts Rack::Cluster::VERSION
63
+ exit
64
+ end
65
+
66
+ opts.parse! ARGV
67
+ end
68
+
69
+ @command = ARGV.shift
70
+ end
71
+
72
+ def run
73
+ runner_commands = ['start', 'stop', 'restart']
74
+
75
+ if @options[:config_file]
76
+ options_from_config_file = YAML.load_file(@options[:config_file])
77
+ symbolized_options = Hash[options_from_config_file.map { |k, v| [k.to_sym, v] }]
78
+ @options = symbolized_options.merge(@options)
79
+ end
80
+
81
+ if runner_commands.include?(@command)
82
+ Rack::Cluster::Runner.send("#{@command}!", @options)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,151 @@
1
+ require 'rack/cluster'
2
+
3
+ module Rack
4
+ module Cluster
5
+ class Runner
6
+ DEFAULT_OPTIONS = {
7
+ :port => 3000,
8
+ :environment => 'development',
9
+ :servers => 1,
10
+ :server => 'mongrel',
11
+ :pid_file => 'tmp/pids/mongrel.pid',
12
+ :verbose => false,
13
+ :rackup => 'rackup'
14
+ }
15
+
16
+ def initialize(options = {})
17
+ symbolized_options = Hash[options.map { |k, v| [k.to_sym, v] }]
18
+ @options = DEFAULT_OPTIONS.merge(symbolized_options)
19
+ end
20
+
21
+ def self.start!(options = {})
22
+ self.new(options).start
23
+ end
24
+
25
+ def self.stop!(options = {})
26
+ self.new(options).stop
27
+ end
28
+
29
+ def self.restart!(options = {})
30
+ self.new(options).restart
31
+ end
32
+
33
+ def start
34
+ start_port.upto(end_port) do |port|
35
+ if pid_file_for_port_exists?(port) && running_on_port?(port)
36
+ puts "already running on port #{port}"
37
+ next
38
+ end
39
+
40
+ if pid_file_for_port_exists?(port) && !running_on_port?(port)
41
+ puts "cleaning up pid file for port #{port}"
42
+ File.unlink(pid_file_for_port(port))
43
+ end
44
+
45
+ cmd = "#{@options[:rackup]} -D"
46
+ cmd << " -s #{@options[:server]}"
47
+ cmd << " -E #{@options[:environment]}"
48
+ cmd << " -p #{port}"
49
+ cmd << " -P #{pid_file_for_port(port)}"
50
+ cmd << " #{config_ru}" if config_ru
51
+
52
+ puts "starting port #{port}"
53
+ puts "#{cmd}" if verbose
54
+ output = `#{cmd}`
55
+ STDERR.puts(output) unless $?.success?
56
+ end
57
+ end
58
+
59
+ def stop
60
+ start_port.upto(end_port) do |port|
61
+ if pid_file_for_port_exists?(port) && !running_on_port?(port)
62
+ puts "cleaning up pid file for port #{port}"
63
+ File.unlink(pid_file_for_port(port))
64
+ end
65
+
66
+ unless running_on_port?(port)
67
+ puts "no process to stop on port #{port}"
68
+ next
69
+ end
70
+
71
+ pid = File.read(pid_file_for_port(port)).to_i
72
+
73
+ puts "killing #{port}, pid #{pid}"
74
+ Process.kill("KILL", pid)
75
+ end
76
+ end
77
+
78
+ def restart
79
+ stop
80
+ start
81
+ end
82
+
83
+ def config_ru
84
+ config_ru_file = @options[:config_ru]
85
+ config_ru_absolute_path = config_ru_file =~ /^\//
86
+ cwd = @options[:cwd]
87
+
88
+ if config_ru_file && cwd
89
+ if config_ru_absolute_path
90
+ config_ru_file
91
+ else
92
+ File.join(cwd, config_ru_file)
93
+ end
94
+ elsif config_ru_file
95
+ config_ru_file
96
+ elsif cwd
97
+ File.join(cwd, 'config.ru')
98
+ else
99
+ 'config.ru'
100
+ end
101
+ end
102
+
103
+ def servers
104
+ @options[:servers].to_i != 0 ? @options[:servers].to_i : 1
105
+ end
106
+
107
+ def start_port
108
+ @options[:port].to_i
109
+ end
110
+
111
+ def end_port
112
+ start_port + (servers - 1)
113
+ end
114
+
115
+ def pid_file_for_port(port)
116
+ return unless @options[:pid_file]
117
+
118
+ pid_file_dir = File.dirname(@options[:pid_file])
119
+ pid_file_ext = File.extname(@options[:pid_file])
120
+ pid_file_basename = File.basename(@options[:pid_file], pid_file_ext)
121
+
122
+ full_pid_file_dir = if @options[:cwd]
123
+ File.join(@options[:cwd], pid_file_dir)
124
+ else
125
+ pid_file_dir
126
+ end
127
+
128
+ File.join(full_pid_file_dir, "#{pid_file_basename}.#{port}#{pid_file_ext}")
129
+ end
130
+
131
+ def pid_file_for_port_exists?(port)
132
+ return false unless @options[:pid_file]
133
+ File.exists?(pid_file_for_port(port))
134
+ end
135
+
136
+ def running_on_port?(port)
137
+ return false unless pid_file_for_port_exists?(port)
138
+
139
+ pid = File.read(pid_file_for_port(port)).to_i
140
+ processes = `ps -p #{pid} -o cmd=`
141
+
142
+ return true if processes =~ /rackup/
143
+ false
144
+ end
145
+
146
+ def verbose
147
+ @options[:verbose]
148
+ end
149
+ end
150
+ end
151
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-cluster
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Carmelo Briones
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-26 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.0
24
+ version:
25
+ description:
26
+ email: ryan.briones@brionesandco.com
27
+ executables:
28
+ - rack-cluster
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - Rakefile
35
+ - README.markdown
36
+ - bin/rack-cluster
37
+ - lib/rack/cluster/command.rb
38
+ - lib/rack/cluster/runner.rb
39
+ - lib/rack/cluster.rb
40
+ has_rdoc: true
41
+ homepage: http://github.com/ryanbriones/rack-cluster
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.3.5
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: tools for managing multiple rackup processes
68
+ test_files: []
69
+