capistrano-atlas 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +215 -0
- data/Rakefile +5 -0
- data/capistrano-atlas.gemspec +32 -0
- data/lib/capistrano/atlas.rb +27 -0
- data/lib/capistrano/atlas/compatibility.rb +37 -0
- data/lib/capistrano/atlas/dsl.rb +157 -0
- data/lib/capistrano/atlas/recipe.rb +49 -0
- data/lib/capistrano/atlas/templates/crontab.erb +1 -0
- data/lib/capistrano/atlas/templates/csr_config.erb +10 -0
- data/lib/capistrano/atlas/templates/logrotate.erb +9 -0
- data/lib/capistrano/atlas/templates/maintenance.html.erb +26 -0
- data/lib/capistrano/atlas/templates/nginx.erb +64 -0
- data/lib/capistrano/atlas/templates/nginx_site.erb +97 -0
- data/lib/capistrano/atlas/templates/pgpass.erb +1 -0
- data/lib/capistrano/atlas/templates/postgresql-backup-logrotate.erb +11 -0
- data/lib/capistrano/atlas/templates/puma.rb.erb +22 -0
- data/lib/capistrano/atlas/templates/puma_init.erb +43 -0
- data/lib/capistrano/atlas/templates/rbenv_bashrc +4 -0
- data/lib/capistrano/atlas/templates/sidekiq_init.erb +100 -0
- data/lib/capistrano/atlas/templates/ssl_setup +43 -0
- data/lib/capistrano/atlas/templates/version.rb.erb +3 -0
- data/lib/capistrano/atlas/version.rb +5 -0
- data/lib/capistrano/tasks/aptitude.rake +111 -0
- data/lib/capistrano/tasks/bundler.rake +31 -0
- data/lib/capistrano/tasks/crontab.rake +14 -0
- data/lib/capistrano/tasks/defaults.rake +137 -0
- data/lib/capistrano/tasks/dotenv.rake +57 -0
- data/lib/capistrano/tasks/logrotate.rake +16 -0
- data/lib/capistrano/tasks/maintenance.rake +28 -0
- data/lib/capistrano/tasks/migrate.rake +29 -0
- data/lib/capistrano/tasks/nginx.rake +25 -0
- data/lib/capistrano/tasks/postgresql.rake +149 -0
- data/lib/capistrano/tasks/provision.rake +18 -0
- data/lib/capistrano/tasks/puma.rake +67 -0
- data/lib/capistrano/tasks/rake.rake +20 -0
- data/lib/capistrano/tasks/rbenv.rake +104 -0
- data/lib/capistrano/tasks/seed.rake +16 -0
- data/lib/capistrano/tasks/sidekiq.rake +42 -0
- data/lib/capistrano/tasks/ssl.rake +57 -0
- data/lib/capistrano/tasks/ufw.rake +32 -0
- data/lib/capistrano/tasks/user.rake +32 -0
- data/lib/capistrano/tasks/version.rake +34 -0
- metadata +161 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Usage:
|
4
|
+
#
|
5
|
+
# ssl_setup [--self] <name> <csr_config>
|
6
|
+
#
|
7
|
+
# This script is used to generate key and CSR for use HTTPS in Nginx.
|
8
|
+
#
|
9
|
+
# --self Generate self-signed certificate in addition to key and CSR.
|
10
|
+
# name Output files will be named as <name>.key and <name>.csr.
|
11
|
+
# csr_config Path to file that specifies CSR information. See below.
|
12
|
+
#
|
13
|
+
# CSR configuration format:
|
14
|
+
#
|
15
|
+
# [ req ]
|
16
|
+
# distinguished_name="req_distinguished_name"
|
17
|
+
# prompt="no"
|
18
|
+
#
|
19
|
+
# [ req_distinguished_name ]
|
20
|
+
# C="US"
|
21
|
+
# ST="California"
|
22
|
+
# L="San Francisco"
|
23
|
+
# O="Example Company"
|
24
|
+
# CN="www.example.com"
|
25
|
+
|
26
|
+
if [[ $1 == --self ]]; then
|
27
|
+
SELF_SIGN=1
|
28
|
+
shift
|
29
|
+
fi
|
30
|
+
|
31
|
+
KEY_NAME=$1
|
32
|
+
CSR_CONFIG=$2
|
33
|
+
|
34
|
+
openssl req -config $CSR_CONFIG -new -newkey rsa:2048 -nodes -keyout ${KEY_NAME}.key -out ${KEY_NAME}.csr
|
35
|
+
chmod 600 ${KEY_NAME}.key ${KEY_NAME}.csr
|
36
|
+
echo "Created ${KEY_NAME}.key"
|
37
|
+
echo "Created ${KEY_NAME}.csr"
|
38
|
+
|
39
|
+
if [[ -n $SELF_SIGN ]]; then
|
40
|
+
openssl x509 -req -days 365 -in ${KEY_NAME}.csr -signkey ${KEY_NAME}.key -out ${KEY_NAME}.crt
|
41
|
+
chmod 600 ${KEY_NAME}.crt
|
42
|
+
echo "Created ${KEY_NAME}.crt (self-signed)"
|
43
|
+
fi
|
@@ -0,0 +1,111 @@
|
|
1
|
+
atlas_recipe :aptitude do
|
2
|
+
during :provision, %w(upgrade install)
|
3
|
+
before "provision:14_04", "atlas:aptitude:install_software_properties"
|
4
|
+
before "provision:14_04", "atlas:aptitude:install_postgres_repo"
|
5
|
+
before "provision:14_04", "atlas:aptitude:change_postgres_packages"
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace :atlas do
|
9
|
+
namespace :aptitude do
|
10
|
+
|
11
|
+
desc "Run `aptitude update` and then run `aptitude safe-upgrade`"
|
12
|
+
task :upgrade do
|
13
|
+
privileged_on roles(:all) do |host|
|
14
|
+
_update
|
15
|
+
_safe_upgrade
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
desc "Run `aptitude install` for packages required by the roles of "\
|
21
|
+
"each server."
|
22
|
+
task :install do
|
23
|
+
privileged_on roles(:all) do |host|
|
24
|
+
packages_to_install = []
|
25
|
+
repos_to_add = []
|
26
|
+
|
27
|
+
_each_package(host) do |pkg, repo|
|
28
|
+
unless _already_installed?(pkg)
|
29
|
+
repos_to_add << repo unless repo.nil?
|
30
|
+
packages_to_install << pkg
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
repos_to_add.uniq.each { |repo| _add_repository(repo) }
|
35
|
+
_update
|
36
|
+
packages_to_install.uniq.each { |pkg| _install(pkg) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Add the official apt repository for PostgreSQL"
|
41
|
+
task :install_postgres_repo do
|
42
|
+
privileged_on roles(:all) do |host|
|
43
|
+
_add_repository(
|
44
|
+
"deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main",
|
45
|
+
:key => "https://www.postgresql.org/media/keys/ACCC4CF8.asc")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Change 12.04 PostgreSQL package requirements to 14.04 versions"
|
50
|
+
task :change_postgres_packages do
|
51
|
+
packages = fetch(:atlas_aptitude_packages, {})
|
52
|
+
packages = Hash[packages.map do |key, value|
|
53
|
+
[key.sub(/@ppa:pitti\/postgresql$/, ""), value]
|
54
|
+
end]
|
55
|
+
set(:atlas_aptitude_packages, packages)
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "Install package needed for apt-add-repository on 14.04"
|
59
|
+
task :install_software_properties do
|
60
|
+
privileged_on roles(:all) do |host|
|
61
|
+
unless _already_installed?("software-properties-common")
|
62
|
+
_install("software-properties-common")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def _already_installed?(pkg)
|
68
|
+
test(:sudo, "dpkg", "-s", pkg, "2>/dev/null", "|", :grep, "-q 'ok installed'")
|
69
|
+
end
|
70
|
+
|
71
|
+
def _add_repository(repo, options={})
|
72
|
+
unless _already_installed?("python-software-properties")
|
73
|
+
_install("python-software-properties")
|
74
|
+
end
|
75
|
+
execute :sudo, "apt-add-repository", "-y '#{repo}'"
|
76
|
+
|
77
|
+
if (key = options.fetch(:key, nil))
|
78
|
+
execute "wget --prefer-family=IPv4 --quiet -O - #{key} | sudo apt-key add -"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def _install(pkg)
|
83
|
+
with :debian_frontend => "noninteractive" do
|
84
|
+
execute :sudo, "aptitude", "-y -q install", pkg
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def _update
|
89
|
+
with :debian_frontend => "noninteractive" do
|
90
|
+
execute :sudo, "aptitude", "-q -q -y update"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def _safe_upgrade
|
95
|
+
with :debian_frontend => "noninteractive" do
|
96
|
+
execute :sudo, "aptitude", "-q -q -y safe-upgrade"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def _each_package(host)
|
101
|
+
return to_enum(:_each_package, host) unless block_given?
|
102
|
+
hostname = host.hostname
|
103
|
+
fetch(:atlas_aptitude_packages).each do |package_spec, *role_list|
|
104
|
+
next unless roles(*role_list.flatten).map(&:hostname).include?(hostname)
|
105
|
+
|
106
|
+
pkg, repo = package_spec.split("@")
|
107
|
+
yield(pkg, repo)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
atlas_recipe :bundler do
|
2
|
+
prior_to "bundler:install", "gem_install"
|
3
|
+
end
|
4
|
+
|
5
|
+
namespace :atlas do
|
6
|
+
namespace :bundler do
|
7
|
+
desc "Install correct version of bundler based on Gemfile.lock"
|
8
|
+
task :gem_install do
|
9
|
+
install_command = fetch(:atlas_bundler_gem_install_command, nil)
|
10
|
+
next unless install_command
|
11
|
+
|
12
|
+
on fetch(:bundle_servers) do
|
13
|
+
within release_path do
|
14
|
+
if (bundled_with = capture_bundled_with)
|
15
|
+
execute "#{install_command} -v #{bundled_with}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def capture_bundled_with
|
22
|
+
lockfile = fetch(:atlas_bundler_lockfile, "Gemfile.lock")
|
23
|
+
return unless test "[ -f #{release_path.join(lockfile)} ]"
|
24
|
+
|
25
|
+
ruby_expr = 'puts $<.read[/BUNDLED WITH\n (\S+)$/, 1]'
|
26
|
+
version = capture :ruby, "-e", ruby_expr.shellescape, lockfile
|
27
|
+
version.strip!
|
28
|
+
version.empty? ? nil : version
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
atlas_recipe :crontab do
|
2
|
+
during "deploy:published", "atlas:crontab"
|
3
|
+
end
|
4
|
+
|
5
|
+
namespace :atlas do
|
6
|
+
desc "Install crontab using crontab.erb template"
|
7
|
+
task :crontab do
|
8
|
+
on roles(:cron) do
|
9
|
+
tmp_file = "/tmp/crontab"
|
10
|
+
template "crontab.erb", tmp_file
|
11
|
+
execute "crontab #{tmp_file} && rm #{tmp_file}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
namespace :load do
|
2
|
+
task :defaults do
|
3
|
+
|
4
|
+
set :atlas_recipes, %w(
|
5
|
+
aptitude
|
6
|
+
bundler
|
7
|
+
crontab
|
8
|
+
dotenv
|
9
|
+
logrotate
|
10
|
+
migrate
|
11
|
+
nginx
|
12
|
+
postgresql
|
13
|
+
rbenv
|
14
|
+
seed
|
15
|
+
ssl
|
16
|
+
ufw
|
17
|
+
user
|
18
|
+
version
|
19
|
+
)
|
20
|
+
|
21
|
+
set :atlas_privileged_user, "root"
|
22
|
+
|
23
|
+
set :atlas_aptitude_packages,
|
24
|
+
"build-essential" => :all,
|
25
|
+
"curl" => :all,
|
26
|
+
"debian-goodies" => :all,
|
27
|
+
"git-core" => :all,
|
28
|
+
"libpq-dev@ppa:pitti/postgresql" => :all,
|
29
|
+
"libreadline-gplv2-dev" => :all,
|
30
|
+
"libssl-dev" => :all,
|
31
|
+
"libxml2" => :all,
|
32
|
+
"libxml2-dev" => :all,
|
33
|
+
"libxslt1-dev" => :all,
|
34
|
+
"nginx@ppa:nginx/stable" => :web,
|
35
|
+
"nodejs@ppa:chris-lea/node.js" => :all,
|
36
|
+
"ntp" => :all,
|
37
|
+
"postgresql-client@ppa:pitti/postgresql" => :all,
|
38
|
+
"postgresql@ppa:pitti/postgresql" => :db,
|
39
|
+
"tklib" => :all,
|
40
|
+
"ufw" => :all,
|
41
|
+
"zlib1g-dev" => :all
|
42
|
+
|
43
|
+
set :atlas_bundler_lockfile, "Gemfile.lock"
|
44
|
+
set :atlas_bundler_gem_install_command,
|
45
|
+
"gem install bundler --conservative --no-document"
|
46
|
+
|
47
|
+
set :atlas_dotenv_keys, %w(rails_secret_key_base postmark_api_key)
|
48
|
+
set :atlas_dotenv_filename, -> { ".env.#{fetch(:rails_env)}" }
|
49
|
+
|
50
|
+
set :atlas_log_file, "log/capistrano.log"
|
51
|
+
|
52
|
+
set :atlas_nginx_force_https, false
|
53
|
+
set :atlas_nginx_redirect_hosts, {}
|
54
|
+
|
55
|
+
set :atlas_puma_threads, "0, 8"
|
56
|
+
set :atlas_puma_workers, 2
|
57
|
+
set :atlas_puma_timeout, 30
|
58
|
+
set :atlas_puma_config, ->{ "#{current_path}/config/puma.rb" }
|
59
|
+
set :atlas_puma_stdout_log, ->{ "#{current_path}/log/puma.stdout.log" }
|
60
|
+
set :atlas_puma_stderr_log, ->{ "#{current_path}/log/puma.stderr.log" }
|
61
|
+
set :atlas_puma_pid, ->{ "#{current_path}/tmp/pids/puma.pid" }
|
62
|
+
|
63
|
+
ask :atlas_postgresql_password, nil, :echo => false
|
64
|
+
set :atlas_postgresql_pool_size, 5
|
65
|
+
set :atlas_postgresql_host, "localhost"
|
66
|
+
set :atlas_postgresql_database,
|
67
|
+
-> { "#{application_basename}_#{fetch(:rails_env)}" }
|
68
|
+
set :atlas_postgresql_user, -> { application_basename }
|
69
|
+
set :atlas_postgresql_pgpass_path,
|
70
|
+
proc{ "#{shared_path}/config/pgpass" }
|
71
|
+
set :atlas_postgresql_backup_path, -> {
|
72
|
+
"#{shared_path}/backups/postgresql-dump.dmp"
|
73
|
+
}
|
74
|
+
set :atlas_postgresql_backup_exclude_tables, []
|
75
|
+
set :atlas_postgresql_dump_options, -> {
|
76
|
+
options = fetch(:atlas_postgresql_backup_exclude_tables).map do |t|
|
77
|
+
"-T #{t.shellescape}"
|
78
|
+
end
|
79
|
+
options.join(" ")
|
80
|
+
}
|
81
|
+
|
82
|
+
set :atlas_rbenv_ruby_version, -> { IO.read(".ruby-version").strip }
|
83
|
+
set :atlas_rbenv_vars, -> {
|
84
|
+
{
|
85
|
+
"RAILS_ENV" => fetch(:rails_env),
|
86
|
+
"PGPASSFILE" => fetch(:atlas_postgresql_pgpass_path)
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
set :atlas_sidekiq_concurrency, 25
|
91
|
+
set :atlas_sidekiq_role, :sidekiq
|
92
|
+
|
93
|
+
ask :atlas_ssl_csr_country, "US"
|
94
|
+
ask :atlas_ssl_csr_state, "California"
|
95
|
+
ask :atlas_ssl_csr_city, "San Francisco"
|
96
|
+
ask :atlas_ssl_csr_org, "Example Company"
|
97
|
+
ask :atlas_ssl_csr_name, "www.example.com"
|
98
|
+
|
99
|
+
# WARNING: misconfiguring firewall rules could lock you out of the server!
|
100
|
+
set :atlas_ufw_rules,
|
101
|
+
"allow ssh" => :all,
|
102
|
+
"allow http" => :web,
|
103
|
+
"allow https" => :web
|
104
|
+
|
105
|
+
|
106
|
+
set :bundle_binstubs, false
|
107
|
+
set :bundle_flags, "--deployment --retry=3 --quiet"
|
108
|
+
set :bundle_path, -> { shared_path.join("bundle") }
|
109
|
+
set :deploy_to, -> { "/home/deployer/apps/#{fetch(:application)}" }
|
110
|
+
set :keep_releases, 10
|
111
|
+
set :linked_dirs, -> {
|
112
|
+
["public/#{fetch(:assets_prefix, 'assets')}"] +
|
113
|
+
%w(
|
114
|
+
.bundle
|
115
|
+
log
|
116
|
+
tmp/pids
|
117
|
+
tmp/cache
|
118
|
+
tmp/sockets
|
119
|
+
public/.well-known
|
120
|
+
public/system
|
121
|
+
)
|
122
|
+
}
|
123
|
+
set :linked_files, -> {
|
124
|
+
[fetch(:atlas_dotenv_filename)] +
|
125
|
+
%w(
|
126
|
+
config/database.yml
|
127
|
+
config/unicorn.rb
|
128
|
+
)
|
129
|
+
}
|
130
|
+
set :log_level, :debug
|
131
|
+
set :migration_role, :app
|
132
|
+
set :rails_env, -> { fetch(:stage) }
|
133
|
+
set :ssh_options, :compression => true, :keepalive => true
|
134
|
+
|
135
|
+
SSHKit.config.command_map[:rake] = "bundle exec rake"
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
atlas_recipe :dotenv do
|
2
|
+
during "provision", "update"
|
3
|
+
prior_to "deploy:publishing", "update"
|
4
|
+
end
|
5
|
+
|
6
|
+
namespace :atlas do
|
7
|
+
namespace :dotenv do
|
8
|
+
desc "Replace/create .env file with values provided at console"
|
9
|
+
task :replace do
|
10
|
+
set_up_prompts
|
11
|
+
|
12
|
+
on release_roles(:all) do
|
13
|
+
update_dotenv_file
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Update .env file with any missing values"
|
18
|
+
task :update do
|
19
|
+
set_up_prompts
|
20
|
+
|
21
|
+
on release_roles(:all), :in => :sequence do
|
22
|
+
existing_env = if test("[ -f #{shared_dotenv_path} ]")
|
23
|
+
download!(shared_dotenv_path)
|
24
|
+
end
|
25
|
+
update_dotenv_file(existing_env.is_a?(String) ? existing_env : "")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def shared_dotenv_path
|
30
|
+
"#{shared_path}/#{fetch(:atlas_dotenv_filename)}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_up_prompts
|
34
|
+
fetch(:atlas_dotenv_keys).each do |key|
|
35
|
+
if key.to_s =~ /key|token|secret|password|pepper/i
|
36
|
+
ask(key, nil, :echo => false)
|
37
|
+
else
|
38
|
+
ask(key, nil)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_dotenv_file(existing="")
|
44
|
+
updated = existing.dup
|
45
|
+
|
46
|
+
fetch(:atlas_dotenv_keys).each do |key|
|
47
|
+
next if existing =~ /^#{Regexp.escape(key.upcase)}=/
|
48
|
+
updated << "\n" unless updated.end_with?("\n")
|
49
|
+
updated << "#{key.upcase}=#{fetch(key)}\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
unless existing == updated
|
53
|
+
put(updated, shared_dotenv_path, :mode => "600")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
atlas_recipe :logrotate do
|
2
|
+
during :provision, "atlas:logrotate"
|
3
|
+
end
|
4
|
+
|
5
|
+
namespace :atlas do
|
6
|
+
desc "Configure logrotate for Rails logs"
|
7
|
+
task :logrotate do
|
8
|
+
privileged_on release_roles(:all) do
|
9
|
+
template "logrotate.erb",
|
10
|
+
"/etc/logrotate.d/#{application_basename}-logs",
|
11
|
+
:mode => 644,
|
12
|
+
:owner => "root:root",
|
13
|
+
:sudo => true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
atlas_recipe :maintenance do
|
2
|
+
# No hooks for this recipe
|
3
|
+
end
|
4
|
+
|
5
|
+
namespace :atlas do
|
6
|
+
namespace :maintenance do
|
7
|
+
desc "Tell nginx to display a 503 page for all web requests, using the "\
|
8
|
+
"maintenance.html.erb template"
|
9
|
+
task :enable do
|
10
|
+
on roles(:web) do
|
11
|
+
reason = ENV["REASON"]
|
12
|
+
deadline = ENV["DEADLINE"]
|
13
|
+
|
14
|
+
template "maintenance.html.erb",
|
15
|
+
"#{current_path}/public/system/maintenance.html",
|
16
|
+
:binding => binding,
|
17
|
+
:mode => "644"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Remove the 503 page"
|
22
|
+
task :disable do
|
23
|
+
on roles(:web) do
|
24
|
+
execute :rm, "-f", "#{current_path}/public/system/maintenance.html"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|