rack-cluster 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+