Skip to content

Commit

Permalink
initial rails app cookbook
Browse files Browse the repository at this point in the history
  • Loading branch information
dje committed Jun 13, 2011
0 parents commit f3f693f
Show file tree
Hide file tree
Showing 23 changed files with 431 additions and 0 deletions.
47 changes: 47 additions & 0 deletions README.md
@@ -0,0 +1,47 @@
Description
===========

A collection of [Heavy Water Software Inc's](http://hw-ops.com)
evolving opinions on best practises for configuring and integrating a
Rails application.

Requirements
============

A Rails application code repository that is configured to manage gem
dependencies via Bundler.

Attributes
==========

* `node["app"]["repository"] ` - Code to deploy (defaults to an
example Rails 3.1 app)
* `node["db"]["adapter"]` - Database such as postgresql, mysql, or
sqlite3 (default)


Usage
=====

Include "recipe[app::deploy]" in a run list to install an example
Rails 3.1 application backed by a SQLite3 database.

If you're cloning a private repository add a deploy key at
app/files/default/deploy_key_id_rsa

A more practical case might be to create an "app" role:

```ruby
name "app"
description "My Rails app"

run_list [ "role[base]",
"recipe[postgresql::server]",
"recipe[app::deploy]" ]

default_attributes( "app" => {
"repository" => "git@github.com:me/myapp.git",
"use_deploy_key" => true },
"db" => {
"adapter" => "postgresql" } )
```
5 changes: 5 additions & 0 deletions attributes/app.rb
@@ -0,0 +1,5 @@
default[:app][:repository] = "https://github.com/RailsApps/rails3-devise-rspec-cucumber.git"
default[:app][:revision] = "master"
default[:app][:migrate] = true
default[:app][:rails_env] = "production"
default[:app][:use_deploy_key] = false
5 changes: 5 additions & 0 deletions attributes/bluepill.rb
@@ -0,0 +1,5 @@
default[:bluepill][:start_grace_time] = 16
default[:bluepill][:stop_grace_time] = 8
default[:bluepill][:restart_grace_time] = 24
default[:bluepill][:mem_usage_mb] = 256
default[:bluepill][:cpu_usage_percent] = 80
5 changes: 5 additions & 0 deletions attributes/db.rb
@@ -0,0 +1,5 @@
default[:db][:adapter] = "sqlite3"
default[:db][:name] = "app"
default[:db][:user] = "app"
default[:db][:pass] = "app"
default[:db][:host] = "127.0.0.1"
3 changes: 3 additions & 0 deletions attributes/unicorn.rb
@@ -0,0 +1,3 @@
default[:unicorn][:stand_alone] = true
default[:unicorn][:timeout] = 30
default[:unicorn][:cow_friendly] = false
2 changes: 2 additions & 0 deletions files/default/known_hosts
@@ -0,0 +1,2 @@
|1|3/H+7sv7Es48a30PNHocVqGB6Vo=|NIUB1atncTQJeeHRVlt6jpsGIlE= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
|1|CLmkEH5jmHUXMT2JdPCL79dWSJU=|odQ02l0q5ZrQk4i82gQKcXmM/R4= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
15 changes: 15 additions & 0 deletions metadata.rb
@@ -0,0 +1,15 @@
maintainer "Heavy Water Software Inc."
maintainer_email "darrin@heavywater.ca"
license "Apache 2.0"
description "Installs/Configures a Rails app"
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version "0.1.0"

supports "ubuntu"

depends "xml"
depends "xslt"
depends "imagemagick"

depends "nginx"
depends "bluepill"
15 changes: 15 additions & 0 deletions recipes/bluepill.rb
@@ -0,0 +1,15 @@
include_recipe "bluepill"

template "/etc/bluepill/app.pill" do
source "bluepill.erb"
variables( :environment => node[:app][:rails_env],
:start_grace_time => node[:bluepill][:start_grace_time],
:stop_grace_time => node[:bluepill][:stop_grace_time],
:restart_grace_time => node[:bluepill][:restart_grace_time],
:mem_usage_mb => node[:bluepill][:mem_usage_mb],
:cpu_usage_percent => node[:bluepill][:cpu_usage_percent] )
end

bluepill_service "app" do
action [ :enable, :load, :start ]
end
14 changes: 14 additions & 0 deletions recipes/database.rb
@@ -0,0 +1,14 @@
directory "/var/www/shared/config" do
owner "www-data"
group "www-data"
recursive true
end

case node[:db][:adapter]
when "mysql"
include_recipe "app::db_mysql"
when "postgresql"
include_recipe "app::db_postgres"
when "sqlite3"
include_recipe "app::db_sqlite3"
end
20 changes: 20 additions & 0 deletions recipes/db_mysql.rb
@@ -0,0 +1,20 @@
include_recipe "mysql::client"

mysql_database "create app database" do
host node[:db][:host]
username "root"
password node[:mysql][:server_root_password]
database node[:db][:name]
action :create_db
end

template "/var/www/shared/config/database.yml" do
source "database.yml.erb"
owner "www-data"
group "www-data"
variables( :adapter => node[:db][:adapter],
:name => node[:db][:name],
:user => "root",
:pass => node[:mysql][:server_root_password],
:host => node[:db][:host] )
end
33 changes: 33 additions & 0 deletions recipes/db_postgres.rb
@@ -0,0 +1,33 @@
package "libpq-dev"

template "/var/www/shared/config/database.yml" do
source "database.yml.erb"
owner "www-data"
group "www-data"
variables( :adapter => node[:db][:adapter],
:name => node[:db][:name],
:user => node[:db][:user],
:pass => node[:db][:pass],
:host => node[:db][:host] )
end

execute "create #{node[:db][:user]} database user" do
command "createuser --createdb --no-createrole --no-superuser #{node[:db][:user]} && \
psql --command \"alter role #{node[:db][:user]} with encrypted password '#{node[:db][:pass]}'\""
user "postgres"
not_if do
`sudo -u postgres psql --tuples-only --command=" \
select rolname from pg_roles where \
rolname = '#{node[:db][:user]}'"`.strip == "#{node[:db][:user]}"
end
end

execute "create #{node[:db][:name]} database" do
command "createdb --encoding=UTF8 --owner=#{node[:db][:user]} #{node[:db][:name]}"
user "postgres"
not_if do
`sudo -u postgres psql --tuples-only --command=" \
select datname from pg_database where \
datname = '#{node[:db][:name]}'"`.strip == "#{node[:db][:name]}"
end
end
7 changes: 7 additions & 0 deletions recipes/db_sqlite3.rb
@@ -0,0 +1,7 @@
package "libsqlite3-dev"

template "/var/www/shared/config/database.yml" do
source "database.sqlite3.yml.erb"
owner "www-data"
group "www-data"
end
33 changes: 33 additions & 0 deletions recipes/default.rb
@@ -0,0 +1,33 @@
#
# Cookbook Name:: app
# Recipe:: default
#
# Copyright 2011, Heavy Water Software Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Seems like nokogiri and rmagick are pretty common Rails
# dependencies. Might as well get those out of the way.
include_recipe "xml"
include_recipe "xslt"
include_recipe "imagemagick"

# Apparently Rails 3.1 requires a JavaScript interpreter for execjs
# and node is the most convenient to install.
include_recipe "app::node"

include_recipe "app::database"
include_recipe "app::nginx"
include_recipe "app::unicorn"
include_recipe "app::bluepill"
50 changes: 50 additions & 0 deletions recipes/deploy.rb
@@ -0,0 +1,50 @@
include_recipe "app"

if node[:app][:use_deploy_key]
include_recipe "app::deploy_key"
end

gem_package "bundler"

%w( shared
shared/pids
shared/system
shared/log ).each do |d|
directory "/var/www/#{d}" do
owner "www-data"
group "www-data"
recursive true
end
end

deploy_revision "/var/www" do
repository node[:app][:repository]
revision node[:app][:revision]
user "www-data"
enable_submodules true
migrate node[:app][:migrate]
migration_command "bundle exec rake db:migrate"
environment "RAILS_ENV" => node[:app][:rails_env]
shallow_clone true

before_migrate do
execute "bundle gems" do
command "bundle install " +
"--deployment --without development test " +
"--path /var/www/shared/bundle " +
"--binstubs /var/www/shared/bundle/bin"
user "www-data"
group "www-data"
cwd release_path
end
end

restart do
execute "restart unicorn" do
command "bluepill app restart"
user "root"
end
end

action node[:app][:deploy_action]
end
18 changes: 18 additions & 0 deletions recipes/deploy_key.rb
@@ -0,0 +1,18 @@
directory "/var/www/.ssh" do
owner "www-data"
group "www-data"
recursive true
end

cookbook_file "/var/www/.ssh/id_rsa" do
source "deploy_key_id_rsa"
owner "www-data"
group "www-data"
mode "600"
end

cookbook_file "/var/www/.ssh/known_hosts" do
owner "www-data"
group "www-data"
mode "644"
end
14 changes: 14 additions & 0 deletions recipes/nginx.rb
@@ -0,0 +1,14 @@
include_recipe "nginx"

template "/etc/nginx/sites-available/app" do
source "nginx.erb"
notifies :restart, "service[nginx]"
end

nginx_site "default" do
enable false
end

nginx_site "app" do
enable true
end
12 changes: 12 additions & 0 deletions recipes/node.rb
@@ -0,0 +1,12 @@
package "python-software-properties"

execute "apt-get update" do
action :nothing
end

execute "add-apt-repository ppa:jerome-etienne/neoip" do
not_if "apt-key list | grep 2D83C357"
notifies :run, "execute[apt-get update]", :immediately
end

package "nodejs"
19 changes: 19 additions & 0 deletions recipes/unicorn.rb
@@ -0,0 +1,19 @@
gem_package "red_unicorn" if node[:unicorn][:stand_alone]

directory "/etc/unicorn"

directory "/var/run/unicorn" do
owner "www-data"
group "www-data"
end

template "/etc/unicorn/app.rb" do
source "unicorn.erb"
owner "root"
group "root"
mode "644"
variables( :timeout => node[:unicorn][:timeout],
:cow_friendly => node[:unicorn][:cow_friendly],
:worker_processes => node[:cpu][:total] )
notifies :restart, "bluepill_service[app]"
end
32 changes: 32 additions & 0 deletions templates/default/bluepill.erb
@@ -0,0 +1,32 @@
Bluepill.application("app") do |app|
app.process("unicorn") do |process|
process.pid_file = "/var/run/unicorn/unicorn.pid"
process.working_dir = "/var/www/current"

process.start_command = "red_unicorn --unicorn-exec /usr/local/bin/unicorn --env <%= @environment %> start"
process.stop_command = "red_unicorn --unicorn-exec /usr/local/bin/unicorn stop"
process.restart_command = "red_unicorn --unicorn-exec /usr/local/bin/unicorn restart"

process.uid = process.gid = "www-data"

process.start_grace_time = <%= @start_grace_time %>.seconds
process.stop_grace_time = <%= @stop_grace_time %>.seconds
process.restart_grace_time = <%= @restart_grace_time %>.seconds

process.monitor_children do |child_process|
child_process.stop_command = "kill -QUIT {{PID}}"

child_process.checks( :mem_usage,
:every => 30.seconds,
:below => <%= @mem_usage_mb %>.megabytes,
:times => [3,4],
:fires => :stop )

child_process.checks( :cpu_usage,
:every => 30.seconds,
:below => <%= @cpu_usage_percent %>,
:times => [3,4],
:fires => :stop )
end
end
end
6 changes: 6 additions & 0 deletions templates/default/database.sqlite3.yml.erb
@@ -0,0 +1,6 @@
---
production:
adapter: sqlite3
database: /var/www/shared/production.sqlite3
pool: 5
timeout: 5000
8 changes: 8 additions & 0 deletions templates/default/database.yml.erb
@@ -0,0 +1,8 @@
---
production:
adapter: <%= @adapter %>
database: <%= @name %>
username: <%= @user %>
password: <%= @pass %>
host: <%= @host %>
encoding: UTF8

0 comments on commit f3f693f

Please sign in to comment.