appserver 0.0.1

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