capistrano-atlas 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.
- 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
|