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