capistrano-atlas 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +215 -0
  7. data/Rakefile +5 -0
  8. data/capistrano-atlas.gemspec +32 -0
  9. data/lib/capistrano/atlas.rb +27 -0
  10. data/lib/capistrano/atlas/compatibility.rb +37 -0
  11. data/lib/capistrano/atlas/dsl.rb +157 -0
  12. data/lib/capistrano/atlas/recipe.rb +49 -0
  13. data/lib/capistrano/atlas/templates/crontab.erb +1 -0
  14. data/lib/capistrano/atlas/templates/csr_config.erb +10 -0
  15. data/lib/capistrano/atlas/templates/logrotate.erb +9 -0
  16. data/lib/capistrano/atlas/templates/maintenance.html.erb +26 -0
  17. data/lib/capistrano/atlas/templates/nginx.erb +64 -0
  18. data/lib/capistrano/atlas/templates/nginx_site.erb +97 -0
  19. data/lib/capistrano/atlas/templates/pgpass.erb +1 -0
  20. data/lib/capistrano/atlas/templates/postgresql-backup-logrotate.erb +11 -0
  21. data/lib/capistrano/atlas/templates/puma.rb.erb +22 -0
  22. data/lib/capistrano/atlas/templates/puma_init.erb +43 -0
  23. data/lib/capistrano/atlas/templates/rbenv_bashrc +4 -0
  24. data/lib/capistrano/atlas/templates/sidekiq_init.erb +100 -0
  25. data/lib/capistrano/atlas/templates/ssl_setup +43 -0
  26. data/lib/capistrano/atlas/templates/version.rb.erb +3 -0
  27. data/lib/capistrano/atlas/version.rb +5 -0
  28. data/lib/capistrano/tasks/aptitude.rake +111 -0
  29. data/lib/capistrano/tasks/bundler.rake +31 -0
  30. data/lib/capistrano/tasks/crontab.rake +14 -0
  31. data/lib/capistrano/tasks/defaults.rake +137 -0
  32. data/lib/capistrano/tasks/dotenv.rake +57 -0
  33. data/lib/capistrano/tasks/logrotate.rake +16 -0
  34. data/lib/capistrano/tasks/maintenance.rake +28 -0
  35. data/lib/capistrano/tasks/migrate.rake +29 -0
  36. data/lib/capistrano/tasks/nginx.rake +25 -0
  37. data/lib/capistrano/tasks/postgresql.rake +149 -0
  38. data/lib/capistrano/tasks/provision.rake +18 -0
  39. data/lib/capistrano/tasks/puma.rake +67 -0
  40. data/lib/capistrano/tasks/rake.rake +20 -0
  41. data/lib/capistrano/tasks/rbenv.rake +104 -0
  42. data/lib/capistrano/tasks/seed.rake +16 -0
  43. data/lib/capistrano/tasks/sidekiq.rake +42 -0
  44. data/lib/capistrano/tasks/ssl.rake +57 -0
  45. data/lib/capistrano/tasks/ufw.rake +32 -0
  46. data/lib/capistrano/tasks/user.rake +32 -0
  47. data/lib/capistrano/tasks/version.rake +34 -0
  48. 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,3 @@
1
+ Rails.application.config.version = "<%= git_version[:tag] %>"
2
+ Rails.application.config.version_date = Date.parse("<%= git_version[:date] %>")
3
+ Rails.application.config.version_time = Time.zone.parse("<%= git_version[:time] %>")
@@ -0,0 +1,5 @@
1
+ module Capistrano
2
+ module Atlas
3
+ VERSION = "0.0.1".freeze
4
+ end
5
+ end
@@ -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