appserver 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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Andreas Neuhaus
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ Software), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,83 @@
1
+ Automagic application server configurator
2
+ =========================================
3
+
4
+ Monit/Nginx/Unicorn application server configurator using deployment via git
5
+ (simply git push applications to your server to deploy them).
6
+
7
+ This little tool automatically generates server configs for [Monit][monit],
8
+ [Nginx][nginx] and [Unicorn][unicorn] to host your [Rack][rack]-based (Rails)
9
+ applications. Running it automatically in git post-receive hooks provides
10
+ an automatic deployment of applications whenever the repository is updated
11
+ on the server.
12
+
13
+ Requirements
14
+ ------------
15
+
16
+ A server running [Monit][monit], [Nginx][nginx] and having [Git][git] and
17
+ Ruby with RubyGems installed.
18
+
19
+ Install
20
+ -------
21
+
22
+ gem install appserver
23
+
24
+ Or check out the [repository][repo] on github.
25
+
26
+ Setup
27
+ -----
28
+
29
+ To run applications, you need to initialize an appserver directory first. To
30
+ do so, create an empty directory and run `appserver init` in it.
31
+
32
+ $ mkdir /var/webapps
33
+ $ cd /var/webapps
34
+ $ appserver init
35
+
36
+ An appserver directory holds configuration files and everything needed to run
37
+ multiple applications (application code, temp files, log files, ...). You can
38
+ customize settings by editing the `appserver.yml` configuration file. **All
39
+ other files are updated automatically and should not be modified manually.**
40
+
41
+ Modify your system's Nginx configuration (e.g. `/etc/nginx/nginx.conf` on
42
+ Ubuntu) to include the generated `nginx.conf` **inside a `http` statement**.
43
+ Reload Nginx to apply the configuration changes.
44
+
45
+ */etc/nginx/nginx.conf:*
46
+
47
+
48
+ http {
49
+
50
+ include /var/www/nginx.conf;
51
+ }
52
+
53
+
54
+ Modify your system's Monit configuration (e.g. `/etc/monit/monitrc` on Ubuntu)
55
+ to include the generated `monitrc` at the bottom. Reload Monit to apply the
56
+ configuration changes.
57
+
58
+ */etc/monit/monitrc:*
59
+
60
+
61
+ include /var/webapps/monitrc
62
+
63
+ Deploying an application
64
+ ------------------------
65
+
66
+ to be done...
67
+
68
+ How it works
69
+ ------------
70
+
71
+ to be done...
72
+
73
+ Author
74
+ ------
75
+
76
+ Andreas Neuhaus :: <http://zargony.com/>
77
+
78
+ [repo]: http://github.com/zargony/appserver/
79
+ [monit]: http://mmonit.com/monit/
80
+ [nginx]: http://nginx.com/
81
+ [unicorn]: http://unicorn.bogomips.org/
82
+ [git]: http://git-scm.com/
83
+ [rack]: http://rack.rubyforge.org/
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'optparse'
6
+ require 'appserver'
7
+
8
+ options = {}
9
+ opts = OptionParser.new(nil, 20, ' ') do |opts|
10
+ opts.banner = 'Usage: appserver [options] init|deploy|update [arguments]'
11
+ opts.separator ''
12
+ opts.separator 'appserver [options] init'
13
+ opts.separator ' Initializes an appserver directory. Run this command once in an empty'
14
+ opts.separator ' directory to set it up for deploying applications. After this, you can'
15
+ opts.separator ' customize settings in appserver.yml inside this directory.'
16
+ opts.separator ''
17
+ opts.separator 'appserver [options] deploy <git-repository>'
18
+ opts.separator ' Deploys an application to the appserver directory and updates configurations.'
19
+ opts.separator ' Additionally, a hook is installed to the git repository, that auto-deploys'
20
+ opts.separator ' the application from now on, if sombody pushes to it.'
21
+ opts.separator ''
22
+ opts.separator 'appserver [options] update'
23
+ opts.separator ' Updates all generated configuration files.'
24
+ opts.separator ''
25
+ opts.separator 'Options:'
26
+ opts.on '-d', '--dir PATH', 'Change to the given directory before running the command' do |dir|
27
+ options[:dir] = dir
28
+ end
29
+ opts.on '-f', '--force', 'Force command execution even if it will overwrite files' do
30
+ options[:force] = true
31
+ end
32
+ opts.separator ''
33
+ opts.separator 'Common options:'
34
+ opts.on '-h', '--help', 'Show this message' do
35
+ puts opts; exit
36
+ end
37
+ opts.separator ''
38
+ opts.separator 'See http://github.com/zargony/appserver for more information'
39
+ opts.separator ''
40
+ end
41
+
42
+ args = opts.parse!
43
+
44
+ (puts opts; exit) if args.size < 1
45
+ Appserver::Command.run!(args.shift, args, options)
@@ -0,0 +1,8 @@
1
+ module Appserver
2
+ ROOT = File.expand_path('..', __FILE__)
3
+ autoload :Utils, "#{ROOT}/appserver/utils"
4
+ autoload :Command, "#{ROOT}/appserver/command"
5
+ autoload :Server, "#{ROOT}/appserver/server"
6
+ autoload :App, "#{ROOT}/appserver/app"
7
+ autoload :Repository, "#{ROOT}/appserver/repository"
8
+ end
@@ -0,0 +1,110 @@
1
+ module Appserver
2
+ class App < Struct.new(:server, :name, :unicorn, :environment, :instances,
3
+ :max_cpu_usage, :max_memory_usage, :usage_check_cycles,
4
+ :http_check_timeout, :hostname, :public_dir)
5
+ DEFAULTS = {
6
+ :unicorn => '/usr/local/bin/unicorn',
7
+ :environment => 'production',
8
+ :instances => 3,
9
+ :max_cpu_usage => nil,
10
+ :max_memory_usage => nil,
11
+ :usage_check_cycles => 5,
12
+ :http_check_timeout => 30,
13
+ :hostname => `/bin/hostname -f`.chomp.gsub(/^[^.]+\./, ''),
14
+ :public_dir => 'public',
15
+ }
16
+
17
+ def self.unicorn_config
18
+ File.expand_path('../unicorn.conf.rb', __FILE__)
19
+ end
20
+
21
+ def initialize (server, name, config)
22
+ super()
23
+ self.server, self.name = server, name
24
+ appconfig = (config[:apps] || {})[name.to_sym] || {}
25
+ DEFAULTS.each do |key, default_value|
26
+ self[key] = appconfig[key] || config[key] || default_value
27
+ end
28
+ # Use a subdomain of the default hostname if no hostname was given specifically for this app
29
+ self.hostname = "#{name}.#{hostname}" unless appconfig[:hostname]
30
+ end
31
+
32
+ def dir
33
+ File.join(server.dir, name)
34
+ end
35
+
36
+ def rack_config
37
+ File.join(dir, 'config.ru')
38
+ end
39
+
40
+ def rack?
41
+ File.exist?(rack_config)
42
+ end
43
+
44
+ def pid_file
45
+ File.join(server.tmp_dir, "#{name}.pid")
46
+ end
47
+
48
+ def socket
49
+ File.join(server.tmp_dir, "#{name}.socket")
50
+ end
51
+
52
+ def server_log
53
+ File.join(server.log_dir, "#{name}.server.log")
54
+ end
55
+
56
+ def access_log
57
+ File.join(server.log_dir, "#{name}.access.log")
58
+ end
59
+
60
+ def write_monit_config (f)
61
+ f.puts %Q()
62
+ f.puts %Q(# Application: #{name})
63
+ if rack?
64
+ cyclecheck = usage_check_cycles > 1 ? " for #{usage_check_cycles} cycles" : ''
65
+ f.puts %Q(check process #{name} with pidfile #{expand_path(pid_file)})
66
+ f.puts %Q( start program = "#{unicorn} -E #{environment} -Dc #{self.class.unicorn_config} #{rack_config}")
67
+ f.puts %Q( stop program = "/bin/kill `cat #{expand_path(pid_file)}`")
68
+ f.puts %Q( if totalcpu usage > #{max_cpu_usage}#{cyclecheck} then restart) if max_cpu_usage
69
+ f.puts %Q( if totalmemory usage > #{max_memory_usage}#{cyclecheck} then restart) if max_memory_usage
70
+ f.puts %Q( if failed unixsocket #{expand_path(socket)} protocol http request "/" timeout #{http_check_timeout} seconds then restart) if http_check_timeout > 0
71
+ f.puts %Q( if 5 restarts within 5 cycles then timeout)
72
+ f.puts %Q( group #{name})
73
+ end
74
+ end
75
+
76
+ def write_nginx_config (f)
77
+ f.puts ""
78
+ f.puts "# Application: #{name}"
79
+ if rack?
80
+ f.puts "upstream #{name}_cluster {"
81
+ f.puts " server unix:#{expand_path(socket)} fail_timeout=0;"
82
+ f.puts "}"
83
+ f.puts "server {"
84
+ f.puts " listen 80;"
85
+ f.puts " server_name #{hostname};"
86
+ f.puts " root #{expand_path(public_dir)};"
87
+ f.puts " access_log #{expand_path(access_log)};"
88
+ f.puts " location / {"
89
+ f.puts " proxy_set_header X-Real-IP $remote_addr;"
90
+ f.puts " proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;"
91
+ f.puts " proxy_set_header Host $http_host;"
92
+ f.puts " proxy_redirect off;"
93
+ # TODO: maintenance mode rewriting
94
+ f.puts " try_files $uri/index.html $uri.html $uri @#{name}_cluster;"
95
+ f.puts " error_page 500 502 503 504 /500.html;"
96
+ f.puts " }"
97
+ f.puts " location @#{name}_cluster {"
98
+ f.puts " proxy_pass http://#{name}_cluster;"
99
+ f.puts " }"
100
+ f.puts "}"
101
+ end
102
+ end
103
+
104
+ protected
105
+
106
+ def expand_path (path)
107
+ File.expand_path(path, dir)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,85 @@
1
+ # This is an appserver directory configuration of the "appserver" gem. Use the
2
+ # "appserver" command or visit http://github.com/zargony/appserver for details
3
+
4
+ #
5
+ # SERVER SETTINGS
6
+ # Non application specific. Paths are relative to the appserver directory. The
7
+ # appserver directory is the directory, that contains this configuration file.
8
+ #
9
+
10
+ # Path to the default directory of git repositories that contain the
11
+ # applications to deploy. Defaults to the home directory of the user 'git'
12
+ #repo_dir: /var/git
13
+
14
+ # Path/name of the Monit configuration snippet that should be written
15
+ #monit_conf: monitrc
16
+
17
+ # Command to execute to tell Monit to reload the configuration. Used within
18
+ # the Monit snippet, so this command will be called as root
19
+ #monit_reload: /usr/sbin/monit reload
20
+
21
+ # Path/name of the Nginx configuration snippet that should be written
22
+ #nginx_conf: nginx.conf
23
+
24
+ # Command to execute to tell Nginx to reload the configuration. Used within
25
+ # the Monit snippet, so this command will be called as root
26
+ #nginx_reload: /usr/sbin/nginx -s reload
27
+
28
+
29
+ #
30
+ # APPLICATION SETTINGS
31
+ # Can be either specified globally for all applications or application-
32
+ # specific under the "apps" subtree (see examples at the bottom of this file).
33
+ # Paths are relative to the respective application directory. Every deployed
34
+ # application has it's own directory under the appserver directory.
35
+ #
36
+
37
+ # Name/path of unicorn
38
+ #unicorn: /usr/local/bin/unicorn
39
+
40
+ # Environment to run the application in. Defaults to 'production'
41
+ #environment: production
42
+
43
+ # Default number of application instances (unicorn workers)
44
+ #instances: 3
45
+
46
+ # Let Monit watch the CPU usage of instances and restart them if their
47
+ # CPU usage exceeds this value
48
+ #max_cpu_usage:
49
+
50
+ # Let Monit watch the memory usage of instances and restart them if their
51
+ # memory usage exceeds this value
52
+ #max_memory_usage:
53
+
54
+ # When doing CPU/memory usage checks, only restart an instance if it exceeds
55
+ # a resource for at least this number of Monit cycles
56
+ #usage_check_cycles: 5
57
+
58
+ # Let Monit check periodically, if instances provide an answer to HTTP
59
+ # requests within the given timeout, or restart them if they don't. Set
60
+ # to 0 to disable
61
+ #http_check_timeout: 30
62
+
63
+ # The hostname, Nginx should accept requests for. You most porbably want to
64
+ # specify the hostname for every application below. If an application has no
65
+ # hostname set, a subdomain of this default hostname will be used. Defaults
66
+ # to the system's domainname.
67
+ #hostname: example.com
68
+
69
+ # Path where public static files should be served from. Defaults to the public
70
+ # directory in the application
71
+ #public_dir: public
72
+
73
+
74
+ #
75
+ # APPLICATIONS
76
+ # All application default settings from above can be overridden for every
77
+ # application. You most probably want to set "hostname" to your liking here.
78
+ # Most other settings should do well with their defaults in most cases.
79
+ #
80
+
81
+ #apps:
82
+ # # A simple blog application named "myblog"
83
+ # myblog:
84
+ # hostname: blog.example.com
85
+ # instances: 1
@@ -0,0 +1,43 @@
1
+ module Appserver
2
+ class UnknownCommandError < RuntimeError; end
3
+
4
+ class Command
5
+ def self.run! (*args)
6
+ new(*args).run!
7
+ end
8
+
9
+ attr_reader :command, :arguments, :options
10
+
11
+ def initialize (command, arguments, options = {})
12
+ @command, @arguments, @options = command, arguments, options
13
+ end
14
+
15
+ def run!
16
+ Dir.chdir(options[:dir]) if options[:dir]
17
+
18
+ Server.initialize_dir(options) if command == 'init'
19
+
20
+ server = Server.new(options)
21
+
22
+ case command
23
+ when 'init'
24
+ server.write_configs
25
+ puts 'Initialized appserver directory.'
26
+ puts 'Wrote Monit and Nginx configuration snippets. Make sure to include them into'
27
+ puts 'your system\'s Monit and Nginx configuration to become active.'
28
+
29
+ when 'deploy'
30
+ repository = server.repository(arguments[0])
31
+ # TODO
32
+ repository.install_hook
33
+
34
+ when 'update'
35
+ server.write_configs
36
+ puts 'Wrote Monit and Nginx configuration snippets.'
37
+
38
+ else
39
+ raise UnknownCommandError
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,48 @@
1
+ module Appserver
2
+ class Repository < Struct.new(:server, :dir)
3
+ class InvalidRepositoryError < RuntimeError; end
4
+
5
+ include Utils
6
+
7
+ def initialize (server, dir, config)
8
+ self.server, self.dir = server, dir.chomp('/')
9
+ raise InvalidRepositoryError unless valid?
10
+ end
11
+
12
+ def name
13
+ File.basename(dir, '.git')
14
+ end
15
+
16
+ def valid?
17
+ File.directory?(File.join(dir, 'hooks')) && File.directory?(File.join(dir, 'refs'))
18
+ end
19
+
20
+ def post_receive_hook
21
+ File.join(dir, 'hooks', 'post-receive')
22
+ end
23
+
24
+ def install_hook
25
+ deploy_cmd = "#{File.expand_path($0)} -d #{server.dir} deploy #{dir}"
26
+ puts deploy_cmd
27
+ if !File.exist?(post_receive_hook) || !File.executable?(post_receive_hook)
28
+ puts "Installing git post-receive hook to repository #{dir}..."
29
+ safe_replace_file(post_receive_hook) do |f|
30
+ f.puts '#!/bin/sh'
31
+ f.puts deploy_cmd
32
+ f.chown File.stat(dir).uid, File.stat(dir).gid
33
+ f.chmod 0755
34
+ end
35
+ elsif !File.readlines(post_receive_hook).any? { |line| line =~ /^#{Regexp.escape(deploy_cmd)}/ }
36
+ puts "Couldn't install post-receive hook. Foreign hook script already present in repository #{dir}!"
37
+ else
38
+ #puts "Hook already installed in repository #{dir}"
39
+ end
40
+ end
41
+
42
+ protected
43
+
44
+ def expand_path (path)
45
+ File.expand_path(path, dir)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,136 @@
1
+ require 'etc'
2
+ require 'yaml'
3
+
4
+ module Appserver
5
+ class Server < Struct.new(:dir, :repo_dir, :monit_conf, :monit_reload, :nginx_conf, :nginx_reload)
6
+ class AlreadyInitializedError < RuntimeError; end
7
+ class DirectoryNotEmptyError < RuntimeError; end
8
+ class NotInitializedError < RuntimeError; end
9
+
10
+ include Utils
11
+
12
+ DEFAULTS = {
13
+ :repo_dir => (Etc.getpwnam('git') rescue {})[:dir],
14
+ :monit_conf => 'monitrc',
15
+ :monit_reload => '/usr/sbin/monit reload',
16
+ :nginx_conf => 'nginx.conf',
17
+ :nginx_reload => '/usr/sbin/nginx -s reload',
18
+ }
19
+
20
+ def self.config_file_template
21
+ File.expand_path('../appserver.yml', __FILE__)
22
+ end
23
+
24
+ def self.search_dir (path = Dir.pwd)
25
+ if File.exist?(File.join(path, 'appserver.yml'))
26
+ path
27
+ elsif path =~ %r(/)
28
+ search_dir(path.sub(%r(/[^/]*$), ''))
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ def self.initialize_dir (options = {})
35
+ raise AlreadyInitializedError if search_dir && !options[:force]
36
+ raise DirectoryNotEmptyError if Dir.glob('*') != [] && !options[:force]
37
+ safe_replace_file('appserver.yml') do |f|
38
+ f.puts File.read(config_file_template)
39
+ end
40
+ ['tmp', 'log'].each do |dir|
41
+ Dir.mkdir(dir) if !File.directory?(dir)
42
+ end
43
+ end
44
+
45
+ def initialize (options = {})
46
+ super()
47
+ # Search upwards for the appserver dir
48
+ self.dir = self.class.search_dir
49
+ raise NotInitializedError unless dir
50
+ # Load configuration settings
51
+ @config = load_config(config_file)
52
+ DEFAULTS.each do |key, default_value|
53
+ self[key] = @config[key] || default_value
54
+ end
55
+ end
56
+
57
+ def config_file
58
+ File.join(dir, 'appserver.yml')
59
+ end
60
+
61
+ def tmp_dir
62
+ File.join(dir, 'tmp')
63
+ end
64
+
65
+ def log_dir
66
+ File.join(dir, 'log')
67
+ end
68
+
69
+ def app (name)
70
+ @apps ||= {}
71
+ @apps[name] ||= App.new(self, name, @config)
72
+ end
73
+
74
+ def apps
75
+ Dir.glob(File.join(dir, '*')).select { |f| File.directory?(f) }.map { |f| File.basename(f) }.map { |name| app(name) }
76
+ end
77
+
78
+ def repository (name_or_path)
79
+ path = name_or_path =~ %r(/) ? expand_path(name_or_path) : File.expand_path("#{name_or_path}.git", repo_dir)
80
+ @repositories ||= {}
81
+ @repositories[path] ||= Repository.new(self, path, @config)
82
+ end
83
+
84
+ def repositories
85
+ Dir.glob(File.join(repo_dir, '*.git')).select { |f| File.directory?(f) }.map { |path| repository(path) }
86
+ end
87
+
88
+ def write_configs
89
+ # Write Monit configuration file
90
+ safe_replace_file(monit_conf) do |f|
91
+ f.puts %Q(# Monit configuration automagically generated by the "appserver" gem using)
92
+ f.puts %Q(# the appserver directory config #{expand_path(config_file)})
93
+ f.puts %Q(# Include this file into your system's monitrc (using an include statement))
94
+ f.puts %Q(# to use it. See http://github.com/zargony/appserver for details.)
95
+ # Let Monit reload itself if this configuration changes
96
+ f.puts %Q(check file monit_conf with path #{expand_path(monit_conf)})
97
+ f.puts %Q( if changed checksum then exec "#{monit_reload}")
98
+ # Reload Nginx if its configuration changes
99
+ f.puts %Q(check file nginx_conf with path #{expand_path(nginx_conf)})
100
+ f.puts %Q( if changed checksum then exec "#{nginx_reload}")
101
+ # Add application-specific Monit configuration
102
+ apps.each do |app|
103
+ app.write_monit_config(f)
104
+ end
105
+ end
106
+ # Write Nginx configuration file
107
+ safe_replace_file(nginx_conf) do |f|
108
+ f.puts %Q(# Nginx configuration automagically generated by the "appserver" gem using)
109
+ f.puts %Q(# the appserver directory config #{expand_path(config_file)})
110
+ f.puts %Q(# Include this file into your system's nginx.conf \(using an include statement)
111
+ f.puts %Q(# inside a http statement\) to use it. See http://github.com/zargony/appserver)
112
+ f.puts %Q(# for details.)
113
+ # The default server always responds with 403 Forbidden
114
+ f.puts %Q(server {)
115
+ f.puts %Q( listen 80 default;)
116
+ f.puts %Q( server_name _;)
117
+ f.puts %Q( deny all;)
118
+ f.puts %Q(})
119
+ # Add application-specific Nginx configuration
120
+ apps.each do |app|
121
+ app.write_nginx_config(f)
122
+ end
123
+ end
124
+ end
125
+
126
+ protected
127
+
128
+ def expand_path (path)
129
+ File.expand_path(path, dir)
130
+ end
131
+
132
+ def load_config (filename)
133
+ symbolize_keys(YAML.load_file(filename) || {})
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path('../../appserver', __FILE__)
2
+ # We assume that the last argument to unicorn is the full path to config.ru
3
+ Dir.chdir(File.expand_path('..', Unicorn::HttpServer::START_CTX[:argv][-1]))
4
+ app = Appserver::Server.new.app(File.basename(Dir.pwd))
5
+
6
+ working_directory app.dir
7
+ stderr_path app.server_log
8
+ stdout_path app.server_log
9
+ puts "Appserver unicorn configuration for #{app.dir}"
10
+ pid app.pid_file
11
+ listen "unix:#{app.socket}", :backlog => 64
12
+ #user 'user', 'group'
13
+ worker_processes app.instances
14
+ timeout 30
15
+ preload_app true
16
+
17
+ # Use COW-friendly REE for memory saving, especially with preloaded apps
18
+ # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
19
+ GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
20
+
21
+ before_fork do |server, worker|
22
+ # For preloaded apps, it is highly recommended to disconnect any database
23
+ # connection and reconnect it in the worker
24
+ if server.config[:preload_app]
25
+ ActiveRecord::Base.connection.disconnect! if defined?(ActiveRecord::Base)
26
+ end
27
+
28
+ # The following is only recommended for memory/DB-constrained
29
+ # installations. It is not needed if your system can house
30
+ # twice as many worker_processes as you have configured.
31
+ #
32
+ # # This allows a new master process to incrementally
33
+ # # phase out the old master process with SIGTTOU to avoid a
34
+ # # thundering herd (especially in the "preload_app false" case)
35
+ # # when doing a transparent upgrade. The last worker spawned
36
+ # # will then kill off the old master process with a SIGQUIT.
37
+ # old_pid = "#{server.config[:pid]}.oldbin"
38
+ # if old_pid != server.pid
39
+ # begin
40
+ # sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
41
+ # Process.kill(sig, File.read(old_pid).to_i)
42
+ # rescue Errno::ENOENT, Errno::ESRCH
43
+ # end
44
+ # end
45
+ #
46
+ # # *optionally* throttle the master from forking too quickly by sleeping
47
+ # sleep 1
48
+ end
49
+
50
+ after_fork do |server, worker|
51
+ # Per-process listener ports for debugging/admin/migrations
52
+ #addr = "127.0.0.1:#{9293 + worker.nr}"
53
+ #server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
54
+
55
+ # Reconnect the database connection in the worker (see disconnect above)
56
+ if server.config[:preload_app]
57
+ ActiveRecord::Base.establish_connection if defined?(ActiveRecord::Base)
58
+ # TODO: check for other database mapper and reconnect them (mongo, memcache, redis)
59
+ end
60
+ end
@@ -0,0 +1,34 @@
1
+ require 'tempfile'
2
+
3
+ module Appserver
4
+ module Utils
5
+ def self.included (base)
6
+ base.class_eval do
7
+ extend Methods
8
+ include Methods
9
+ end
10
+ end
11
+
12
+ module Methods
13
+ def safe_replace_file (filename)
14
+ tempfile = Tempfile.new(File.basename(filename) + '.', File.dirname(filename))
15
+ if File.exist?(filename)
16
+ tempfile.chown(File.stat(filename).uid, File.stat(filename).gid)
17
+ tempfile.chmod(File.stat(filename).mode)
18
+ end
19
+ yield tempfile
20
+ tempfile.close
21
+ File.unlink(filename) if File.exist?(filename)
22
+ File.rename(tempfile, filename)
23
+ end
24
+
25
+ def symbolize_keys (hash)
26
+ hash.inject({}) do |memo, (key, value)|
27
+ value = symbolize_keys(value) if Hash === value
28
+ memo[key.to_sym] = value
29
+ memo
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: appserver
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Andreas Neuhaus
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-28 00:00:00 +02:00
18
+ default_executable: appserver
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: unicorn
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 97
30
+ version: "0.97"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: This little tool automatically generates server configs for Monit, Nginx and Unicorn to host your Rack-based (Rails) applications. Running it automatically in git post-receive hooks provides an automatic deployment of applications whenever the repository is updated on the server.
34
+ email: zargony@gmail.com
35
+ executables:
36
+ - appserver
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - LICENSE
41
+ - README.md
42
+ files:
43
+ - LICENSE
44
+ - README.md
45
+ - bin/appserver
46
+ - lib/appserver.rb
47
+ - lib/appserver/app.rb
48
+ - lib/appserver/appserver.yml
49
+ - lib/appserver/command.rb
50
+ - lib/appserver/repository.rb
51
+ - lib/appserver/server.rb
52
+ - lib/appserver/unicorn.conf.rb
53
+ - lib/appserver/utils.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/zargony/appserver
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --charset=UTF-8
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements:
78
+ - nginx v0.7.x or greater
79
+ - monit v5.x or greater
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.6
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Monit/nginx/unicorn application server configurator using deployment via git
85
+ test_files: []
86
+