mutle-rackdapter 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.
data/Manifest ADDED
@@ -0,0 +1,16 @@
1
+ bin/rackdapter
2
+ bin/rackdapter_proxy
3
+ lib/rackdapter/config.rb
4
+ lib/rackdapter/master.rb
5
+ lib/rackdapter/proxy.rb
6
+ lib/rackdapter/rackdapter.rb
7
+ lib/rackdapter/spawner.rb
8
+ lib/rackdapter.rb
9
+ Manifest
10
+ MIT-LICENSE
11
+ rackdapter.gemspec
12
+ Rakefile
13
+ README.rdoc
14
+ test/rackdapter.yml
15
+ test/test_helper.rb
16
+ test/unit/test_config.rb
data/README.rdoc ADDED
@@ -0,0 +1,67 @@
1
+ =Rackdapter
2
+
3
+ ==Disclaimer
4
+
5
+ I'm looking for a better name, esp. since it doesn't actually use Rack.
6
+
7
+ Copyright (c) 2008 Mutwin Kraus (mutwin.kraus@gmail.com)
8
+
9
+ Rackdapter is licensed under the MIT License.
10
+
11
+ ==Usage
12
+
13
+ Starting
14
+ rackdapter /patch/to/rackdapter.yml
15
+
16
+ To restart all instances, send SIGHUP to the rackdapter process.
17
+
18
+ ==Sample config
19
+
20
+ #rackdapter.yml
21
+ proxy:
22
+ log: log/proxy.log
23
+ portal:
24
+ path: /Users/mutle/bs/portal
25
+ environment: development
26
+ backend: mongrel
27
+ type: rails
28
+ port: 3006
29
+ base_port: 23932
30
+ instances: 1
31
+ log: log/rackdapter.<environment>.<port>.log
32
+ blog:
33
+ path: /Users/mutle/bs/blog
34
+ environment: development
35
+ backend: mongrel
36
+ type: rails
37
+ port: 3007
38
+ base_port: 23942
39
+ instances: 1
40
+ log: log/rackdapter.<environment>.<port>.log
41
+ files:
42
+ path: /Users/mutle/bs/user_files
43
+ environment: development
44
+ backend: mongrel
45
+ type: merb
46
+ port: 4000
47
+ base_port: 23952
48
+ instances: 1
49
+ log: log/rackdapter.<environment>.<port>.log
50
+ ads:
51
+ path: /Users/mutle/bs/ads
52
+ environment: development
53
+ backend: mongrel
54
+ type: merb
55
+ port: 4001
56
+ base_port: 23962
57
+ instances: 1
58
+ log: log/rackdapter.<environment>.<port>.log
59
+ layouts:
60
+ path: /Users/mutle/bs/layouts
61
+ environment: development
62
+ backend: mongrel
63
+ type: merb
64
+ port: 4002
65
+ base_port: 23972
66
+ instances: 1
67
+ log: log/rackdapter.<environment>.<port>.log
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('rackdapter', '0.0.1') do |p|
6
+ p.description = "rackdapter"
7
+ p.gemspec_name = "rackdapter.gemspec"
8
+ p.url = "http://github.com/mutle/rackdapter"
9
+ p.author = "Mutwin Kraus"
10
+ p.email = "mutwin.kraus@gmail.com"
11
+ p.ignore_pattern = ["tmp/*", "log/*"]
12
+ p.development_dependencies = []
13
+ p.runtime_dependencies = ["eventmachine"]
14
+ end
data/bin/rackdapter ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '../lib/rackdapter')
4
+ Rackdapter::Master.new(ARGV.first || 'rackdapter.yml')
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $PROXY = 1
4
+ require File.join(File.dirname(__FILE__), '../lib/rackdapter')
5
+ Rackdapter::Proxy.new(ARGV.first || 'rackdapter.yml')
@@ -0,0 +1,56 @@
1
+ require 'yaml'
2
+
3
+ module Rackdapter
4
+
5
+ class << self
6
+ attr_accessor :config_path
7
+ end
8
+
9
+ def self.config
10
+ @config ||= Config.new(@config_path)
11
+ end
12
+
13
+ class Config
14
+
15
+ attr_accessor :apps, :log
16
+
17
+ def initialize(path='')
18
+ @apps = {}
19
+ config = YAML.load(File.read(path))
20
+ if config
21
+ config.each do |k,v|
22
+ @apps[k.to_sym] = v
23
+ end
24
+ @apps[:proxy] ||= {}
25
+ @apps[:proxy].merge!("type" => "proxy", "instances" => 1, "base_port" => 0)
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ module Configuration
32
+ def configure(config_path)
33
+ Rackdapter.config_path = config_path
34
+ Rackdapter.config
35
+ end
36
+
37
+ def log(message)
38
+ puts message
39
+ end
40
+
41
+ def config
42
+ Rackdapter.config
43
+ end
44
+
45
+ def load_apps
46
+ Rackdapter.config.apps.each do |name,app|
47
+ apps[name] = Application.new(app.merge(:name => name))
48
+ end
49
+ end
50
+
51
+ def apps
52
+ @apps ||= {}
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,37 @@
1
+ module Rackdapter
2
+
3
+ class Master
4
+
5
+ include Rackdapter::Configuration
6
+
7
+ def initialize(config_path)
8
+ log "Loading rackdapter config at #{config_path}"
9
+ configure(config_path)
10
+ log "Configuration loaded, starting master server"
11
+ start
12
+ end
13
+
14
+ def start
15
+ load_apps
16
+ run
17
+ end
18
+
19
+ private
20
+
21
+ def all_apps(action)
22
+ apps.each do |name, app|
23
+ app.send(action.to_sym)
24
+ end
25
+ end
26
+
27
+ def run
28
+ Signal.trap("INT") { all_apps(:stop); exit(0) }
29
+ Signal.trap("TERM") { all_apps(:stop); exit(0) }
30
+ Signal.trap("HUP") { all_apps(:restart) }
31
+ EventMachine::run {
32
+ EventMachine::add_timer(5) { all_apps(:ensure_running) }
33
+ }
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,113 @@
1
+ module Rackdapter
2
+
3
+ class InboundProxyConnection < EventMachine::Connection
4
+
5
+ def initialize(*args)
6
+ @app = args.first
7
+ super
8
+ end
9
+
10
+ def client_connection=(con)
11
+ @request = con
12
+ end
13
+
14
+ def post_init
15
+ host,port = "127.0.0.1", @app.next_port
16
+ EventMachine::connect host, port, OutboundProxyConnection, self
17
+ @receive_buffer = ""
18
+ @connected = false
19
+ end
20
+
21
+ def receive_data(data)
22
+ @receive_buffer << data
23
+ if @connected
24
+ send data
25
+ else
26
+ if @request
27
+ @connected = true
28
+ send @receive_buffer
29
+ end
30
+ end
31
+ end
32
+
33
+ def send(data)
34
+ # puts "sending to client: #{data}\n---\n"
35
+ @request.send_data(data)
36
+ end
37
+
38
+ def close
39
+ close_connection_after_writing
40
+ end
41
+
42
+ def unbind
43
+ close_connection
44
+ end
45
+
46
+ end
47
+
48
+ class OutboundProxyConnection < EventMachine::Connection
49
+
50
+ def initialize(*args)
51
+ @response = args.first
52
+ @response.client_connection = self
53
+ super
54
+ end
55
+
56
+ def post_init
57
+ @response_buffer = ""
58
+ end
59
+
60
+ def receive_data(data)
61
+ # puts "sending to proxy: #{data.inspect}\n---\n"
62
+ @response_buffer << data
63
+ @response.send_data(data)
64
+ end
65
+
66
+ def unbind
67
+ @response.close
68
+ end
69
+
70
+ end
71
+
72
+ class Proxy
73
+
74
+ include Rackdapter::Configuration
75
+
76
+ def config
77
+ Rackdapter.config.apps[:proxy]
78
+ end
79
+
80
+ def logger
81
+ unless @logger
82
+ @logger = Logger.new(config['log'] || STDOUT)
83
+ @logger.level = Logger::INFO
84
+ end
85
+ @logger
86
+ end
87
+
88
+ def log(message)
89
+ logger.info message
90
+ end
91
+
92
+ def initialize(config_path)
93
+ configure(config_path)
94
+ log "Starting proxy server with configuration at #{config_path}"
95
+ start
96
+ end
97
+
98
+ def start
99
+ load_apps
100
+ EventMachine::run {
101
+ apps.each do |name,app|
102
+ host,port = "0.0.0.0", app.port
103
+ if port && port > 0
104
+ log "Starting proxy at #{host}:#{port}"
105
+ EventMachine::start_server host, port, InboundProxyConnection, app
106
+ end
107
+ end
108
+ }
109
+ end
110
+
111
+ end
112
+
113
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'eventmachine'
3
+ require 'logger'
4
+
5
+ require 'rackdapter/config'
6
+ require 'rackdapter/spawner'
7
+
8
+ if $PROXY == 1
9
+ require 'rackdapter/proxy'
10
+ else
11
+ require 'rackdapter/master'
12
+ end
@@ -0,0 +1,154 @@
1
+ module Rackdapter
2
+
3
+ class Application
4
+ attr_accessor :name, :path, :environment, :backend, :port, :balancer, :base_port, :instances, :type, :log
5
+
6
+ def initialize(options={})
7
+ options.each do |k,v|
8
+ send("#{k.to_s}=".to_sym, v)
9
+ end
10
+ @app_instances = {}
11
+ ports.each do |port|
12
+ @app_instances[port] = ApplicationInstance.new(self, port)
13
+ end
14
+ end
15
+
16
+ def app_instances
17
+ @app_instances
18
+ end
19
+
20
+ def next_port
21
+ @balancer ||= ProxyBalancer.new(ports)
22
+ @balancer.next_port
23
+ end
24
+
25
+ def ports
26
+ return @ports if @ports
27
+ @ports = []
28
+ instances.times do |i|
29
+ @ports << base_port + i
30
+ end
31
+ @ports
32
+ end
33
+
34
+ def all_instances(&block)
35
+ @app_instances.each do |port,instance|
36
+ instance.send(:instance_eval, &block)
37
+ end
38
+ end
39
+
40
+ %w(ensure_running start stop restart).each do |action|
41
+ eval <<-RUBY
42
+ def #{action}
43
+ all_instances { #{action} }
44
+ end
45
+ RUBY
46
+ end
47
+ end
48
+
49
+ class ProxyBalancer
50
+ def initialize(ports)
51
+ @ports = ports
52
+ @seq = 0
53
+ end
54
+
55
+ def next_port
56
+ port = @ports[@seq]
57
+ @seq = @seq + 1
58
+ @seq = 0 if @seq >= @ports.size
59
+ port
60
+ end
61
+ end
62
+
63
+ def self.app_options(app, port, options)
64
+ out = ''
65
+ log = app.log.gsub(/<port>/, port.to_s).gsub(/<environment>/, app.environment)
66
+ options.each do |k,v|
67
+ out << " -#{k.to_s} #{v}" unless v.blank?
68
+ end
69
+ out << " >> #{log} 2>&1"
70
+ out
71
+ end
72
+
73
+ module RailsBackend
74
+ def self.spawn(app, port)
75
+ Dir.chdir(app.path)
76
+ options = Rackdapter.app_options(app, port, "e" => app.environment, "p" => port)
77
+ exec "mongrel_rails start #{options}"
78
+ end
79
+ end
80
+
81
+ module MerbBackend
82
+ def self.spawn(app, port)
83
+ Dir.chdir(app.path)
84
+ options = Rackdapter.app_options(app, port, "e" => app.environment, "p" => port, "n" => app.name)
85
+ exec "merb #{options}"
86
+ end
87
+ end
88
+
89
+ module ProxyBackend
90
+ def self.spawn(application, port=nil)
91
+ exec "rackdapter_proxy #{Rackdapter.config_path}"
92
+ end
93
+ end
94
+
95
+ class ApplicationInstance
96
+
97
+ def initialize(application, port)
98
+ @application = application
99
+ @port = port
100
+ @backend = case @application.type
101
+ when "merb"
102
+ MerbBackend
103
+ when "rails"
104
+ RailsBackend
105
+ when "proxy"
106
+ ProxyBackend
107
+ end
108
+ end
109
+
110
+ def ensure_running
111
+ start
112
+ end
113
+
114
+ def start
115
+ return if running?
116
+ @pid = fork do
117
+ @backend.spawn(@application, @port)
118
+ end
119
+ puts "Starting #{@application.name}:#{@port} with pid #{@pid}"
120
+ Process.detach(@pid)
121
+ end
122
+
123
+ def stop
124
+ if running?
125
+ puts "Stopping #{@application.name}:#{@port}"
126
+ begin
127
+ Process.kill("TERM", @pid)
128
+ rescue
129
+ puts "Process #{@pid} not found"
130
+ end
131
+ @pid = nil
132
+ end
133
+ end
134
+
135
+ def restart
136
+ puts "Initiating restart of #{@application.name}:#{@port}"
137
+ stop
138
+ start
139
+ end
140
+
141
+ def running?
142
+ return false unless @pid
143
+ found = false
144
+ `ps`.each_line do |line|
145
+ if line =~ /(^| )(#{@pid}) /
146
+ found = true
147
+ break
148
+ end
149
+ end
150
+ found
151
+ end
152
+ end
153
+
154
+ end
data/lib/rackdapter.rb ADDED
@@ -0,0 +1,6 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'rubygems'
4
+ require 'extlib'
5
+
6
+ require 'rackdapter/rackdapter'
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{rackdapter}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Mutwin Kraus"]
9
+ s.date = %q{2008-12-05}
10
+ s.description = %q{rackdapter}
11
+ s.email = %q{mutwin.kraus@gmail.com}
12
+ s.executables = ["rackdapter", "rackdapter_proxy"]
13
+ s.extra_rdoc_files = ["bin/rackdapter", "bin/rackdapter_proxy", "lib/rackdapter/config.rb", "lib/rackdapter/master.rb", "lib/rackdapter/proxy.rb", "lib/rackdapter/rackdapter.rb", "lib/rackdapter/spawner.rb", "lib/rackdapter.rb", "README.rdoc"]
14
+ s.files = ["bin/rackdapter", "bin/rackdapter_proxy", "lib/rackdapter/config.rb", "lib/rackdapter/master.rb", "lib/rackdapter/proxy.rb", "lib/rackdapter/rackdapter.rb", "lib/rackdapter/spawner.rb", "lib/rackdapter.rb", "Manifest", "MIT-LICENSE", "rackdapter.gemspec", "Rakefile", "README.rdoc", "test/rackdapter.yml", "test/test_helper.rb", "test/unit/test_config.rb"]
15
+ s.has_rdoc = true
16
+ s.homepage = %q{http://github.com/mutle/rackdapter}
17
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rackdapter", "--main", "README.rdoc"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{rackdapter}
20
+ s.rubygems_version = %q{1.3.1}
21
+ s.summary = %q{rackdapter}
22
+ s.test_files = ["test/test_helper.rb", "test/unit/test_config.rb"]
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 2
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
30
+ else
31
+ s.add_dependency(%q<eventmachine>, [">= 0"])
32
+ end
33
+ else
34
+ s.add_dependency(%q<eventmachine>, [">= 0"])
35
+ end
36
+ end
@@ -0,0 +1,8 @@
1
+ proxy:
2
+ log: log/proxy.log
3
+ test:
4
+ path: apps/test
5
+ environment: development
6
+ backend: mongrel
7
+ port: 4080
8
+ min_instances: 2
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ gem 'jeremymcanally-context'
5
+ require 'context'
6
+
7
+ gem 'jeremymcanally-matchy'
8
+ require 'matchy'
9
+
10
+
11
+ require File.join(File.dirname(__FILE__), '../lib/rackdapter')
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+
3
+ class UserTest < Test::Unit::TestCase
4
+
5
+ context "Rackdapter::Config" do
6
+
7
+ it "should load the config file" do
8
+ config = Rackdapter::Config.new(File.join(File.dirname(__FILE__), '../rackdapter.yml'))
9
+ config.apps.keys.include?(:test).should == true
10
+ end
11
+
12
+ end
13
+
14
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mutle-rackdapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mutwin Kraus
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-05 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: eventmachine
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ description: rackdapter
25
+ email: mutwin.kraus@gmail.com
26
+ executables:
27
+ - rackdapter
28
+ - rackdapter_proxy
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - bin/rackdapter
33
+ - bin/rackdapter_proxy
34
+ - lib/rackdapter/config.rb
35
+ - lib/rackdapter/master.rb
36
+ - lib/rackdapter/proxy.rb
37
+ - lib/rackdapter/rackdapter.rb
38
+ - lib/rackdapter/spawner.rb
39
+ - lib/rackdapter.rb
40
+ - README.rdoc
41
+ files:
42
+ - bin/rackdapter
43
+ - bin/rackdapter_proxy
44
+ - lib/rackdapter/config.rb
45
+ - lib/rackdapter/master.rb
46
+ - lib/rackdapter/proxy.rb
47
+ - lib/rackdapter/rackdapter.rb
48
+ - lib/rackdapter/spawner.rb
49
+ - lib/rackdapter.rb
50
+ - Manifest
51
+ - MIT-LICENSE
52
+ - rackdapter.gemspec
53
+ - Rakefile
54
+ - README.rdoc
55
+ - test/rackdapter.yml
56
+ - test/test_helper.rb
57
+ - test/unit/test_config.rb
58
+ has_rdoc: true
59
+ homepage: http://github.com/mutle/rackdapter
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --line-numbers
63
+ - --inline-source
64
+ - --title
65
+ - Rackdapter
66
+ - --main
67
+ - README.rdoc
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "1.2"
81
+ version:
82
+ requirements: []
83
+
84
+ rubyforge_project: rackdapter
85
+ rubygems_version: 1.2.0
86
+ signing_key:
87
+ specification_version: 2
88
+ summary: rackdapter
89
+ test_files:
90
+ - test/test_helper.rb
91
+ - test/unit/test_config.rb