Skip to content

Latest commit

 

History

History
1304 lines (955 loc) · 34.2 KB

010_capistrano.md

File metadata and controls

1304 lines (955 loc) · 34.2 KB

We're going to use an automated deployment tool called Capistrano to both do our initial deploy and future deploys. Capistrano is frequently used with Ruby on Rails applications (it is written in Ruby) but can be used with other languages as well.

Setting up the Rails application

  vagrant@workshop $ cd ~/widgetworld

Open up the Gemfile

  vagrant@workshop $ vim Gemfile

Add this content

group :development do
  gem 'capistrano',  '~> 3.1'
  gem 'capistrano-rails', '~> 1.1'
  gem 'capistrano-rvm'
end

Then run:

  vagrant@workshop $ bundle

This will install all the necessary ruby gems for this application, including the ones needed for running Capistrano.

If you receive this error:

  Can't find the 'libpq-fe.h header
  *** extconf.rb failed ***
  Could not create Makefile due to some reason, probably lack of necessary
  libraries and/or headers.

Run this on your development VM:

  vagrant@workshop $ sudo apt-get install postgresql-client libpq5 libpq-dev
  vagrant@workshop $ sudo gem install pg -v '0.17.1'

Then rerun:

  vagrant@workshop $ bundle

If you continue to get the same error, comment out the pg gem line in your Gemfile

  #gem 'pg'

(It was already installed when you ran "sudo gem install pg -v '0.17.1'")

Then rerun:

  vagrant@workshop $ bundle

Installing and Configuring Capistrano

There is also some special additional installation for capistrano. Run this command, which will add some additional Capistrano config files.

  vagrant@workshop $ cap install

Next, open your Capfile in your favorite editor

  vagrant@workshop $ vim Capfile

Uncomment these lines

  # require 'capistrano/rvm'
  # require 'capistrano/bundler'

So it looks like this

  require 'capistrano/rvm'
  require 'capistrano/bundler'

Next, open up the config/deploy.rb file with your favorite text editor

  vagrant@workshop $ vim config/deploy.rb

Change these to lines

set :application, 'my_app_name'
set :repo_url, '[email protected]:me/my_repo.git'

To this

set :application, 'widgetworld'
set :repo_url, '[email protected]:nellshamrell/widgetworld.git'

Then change this line

# set :deploy_to, '/var/www/my_app_name'

To this

 set :deploy_to, '/var/www/widgetworld'

Then uncomment this line

  set :scm, :git

And uncomment this line

  set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml')

Finally, uncomment this line

   set :linked_dirs, fetch(:linked_dirs, []).push('bin', 'log', 'tmp/pids', 'tm    p/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')

And add this sequence of lines right after "namespace :deploy do"

namespace :deploy do


desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
     # This restarts Passenger
      execute :mkdir, '-p', "#{ release_path }/tmp"
     execute :touch, release_path.join('tmp/restart.txt')
   end
 end

 after :publishing, :restart

Modifying our Chef recipe

In the changes we just made to config/deploy.rb, we specified that we would be deploying our application to a directory in /var/www on the VM.

We need to change the owner of this directory to deploy

First, change directories back to your cookbook.

  vagrant@workshop $ cd ~/fresh_start_web_server_repo/cookbooks/my_web_server_cookbook

Let's create a new recipe, this one specifically for the app.

  vagrant@workshop $ chef generate recipe app

Now we need to create the directory where our server specs will live:

  vagrant@workshop $ mkdir -p test/integration/app/serverspec

And create the test file

  vagrant@workshop $ touch test/integration/app/serverspec/app_spec.rb

And we need to be able to access a spec_helper similar to the one living in test/integration/default/serverspec. In this case, let's copy that one into our new integration test directory.

  vagrant@workshop $ cp test/integration/default/serverspec/spec_helper.rb test/integration/app/serverspec

Now let's create some tests, checking that the /var/www directory is owned by deploy and grouped in the deploy group.

Setting ownership of the www directory

test/integration/app/serverspec/app_spec.rb

require 'spec_helper'
describe 'my_web_server_cookbook::app' do
  describe file('/var/www') do
    it { should be_owned_by 'deploy' }
  end

  describe file('/var/www') do
    it { should be_grouped_into 'deploy' }
  end
end

Now we need to add this test suite to our .kitchen.yml file so Test Kitchen will run it.

Open up you .kitchen.yml file

  vagrant@workshop $ vim .kitchen.yml

And add this content:

suites:
- name: default
  run_list:
    - recipe[my_web_server_cookbook::default]
  attributes:
- name: ruby
  run_list:
    - recipe[my_web_server_cookbook::ruby]
  attributes:
- name: passenger
  run_list:
    - recipe[my_web_server_cookbook::passenger]
  attributes:
- name: postgresql
  run_list:
    - recipe[my_web_server_cookbook::postgresql]
  attributes:
- name: user
  run_list:
    - recipe[my_web_server_cookbook::user]
  attributes:
- name: app
  run_list:
    - recipe[my_web_server_cookbook::app]
  attributes:

Next you'll need to create a new test instance for the new suite:

  vagrant@workshop $ kitchen create app-ubuntu-14-04-x64

And then set it up with Chef:

  vagrant@workshop $ kitchen setup app-ubuntu-14-04-x64

Now, run the tests:

  vagrant@workshop $ kitchen verify app-ubuntu-14-04-x64

And they should fail.

Now let's make them pass. Open up recipes/app.rb and add this content:

include_recipe 'my_web_server_cookbook::default'
include_recipe 'my_web_server_cookbook::user'

execute 'change owner and group' do
  command "sudo chown deploy:deploy /var/www"
  action :run
 end

Now apply the changes:

  vagrant@workshop $ kitchen converge app-ubuntu-14-04-x64

And run the tests again

  vagrant@workshop $ kitchen verify app-ubuntu-14-04-x64

And they should pass!

Now we need to add this to our node's run list. Open up nodes/[node's ip address].json and add this recipe to your run list:

  {
    "run_list": [
      "recipe[my_web_server_cookbook::default]",
      "recipe[my_web_server_cookbook::passenger]",
      "recipe[my_web_server_cookbook::ruby]",
      "recipe[my_web_server_cookbook::postgresql]",
      "recipe[my_web_server_cookbook::user]",
      "recipe[my_web_server_cookbook::app]"
    ]
  }

Now, to avoid installing passenger, etc. again, modify your run list so it looks like this:

  {
    "run_list": [
      "recipe[my_web_server_cookbook::app]"
    ]
  }

This way it will only apply changes from our app recipe and avoid a lengthy reconverge.

Now, apply this to your staging node with:

  vagrant@workshop $ knife solo cook root@[your staging instance ip]

Setting Capistrano roles

Now change back to your widgetworld directory

  vagrant@workshop $ cd ~/widgetworld

Take a look at the various deploy configs:

  vagrant@workshop $ ls config/deploy

You should see two configs, each corresponding to a different environment.

  production.rb  staging.rb

Let's use the staging config for right now.

Open up the config/deploy/staging.rb file. Uncomment and change these lines:

   role :app, %w{[email protected]}, my_property: :my_value
   role :web, %w{[email protected] [email protected]}, other_property: :other_value
   role :db,  %w{[email protected]}

To this

(Make sure to substitute the ip address for your testing node)

  role :app, %w{deploy@#{ip_address}}
  role :web, %w{deploy@#{ip_address}}
  role :db,  %w{deploy@#{ip_address}}

And uncomment and change this line

  server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value

To this:

(Make sure to substitute the ip address for your testing node)

  server '#{ip_address}', user: 'deploy', roles: %w{app db web}

Now let's check whether our application is ready to deploy to testing. Run this command:

  vagrant@workshop $ cap staging deploy:check

Whoops, looks like it's prompting for a password. This is because our ssh key is not on the testing instance for the deploy user. Let's get that fixed.

Adding your SSH Key

Change directories back to your chef cookbook.

  vagrant@workshop $ cd ~/fresh_start_chef_repo/cookbooks/my_web_server_cookbook/

Normally, we would add the SSH key through a user databag. For the sake of time, we're going to use a template instead in this workshop. However, a databag would be a better way to do this in a "real world" environment. To learn more about databags, see the Chef documentation on databags.

First, we need to add an .ssh director to the deploy folder in the /home directory. Let's add this into the user recipe. Open up the user test file and add this test

test/integration/user/serverspec/user_spec.rb

  describe file('/home/deploy/.ssh') do
    it { should be_directory }
  end

And run the test:

  vagrant@workshop $ kitchen verify user-ubuntu-14-04-x64

And, as expected, it fails. Now let's make it pass.

Open up recipes/user.rb and add this content.

  execute 'create .ssh directory' do
    command "sudo mkdir /home/deploy/.ssh"
    action :run
    not_if { ::File.exists?("/home/deploy/.ssh")}
  end

Then converge and run the tests again.

  vagrant@workshop $ kitchen converge user-ubuntu-14-04-x64
  vagrant@workshop $ kitchen verify user-ubuntu-14-04-x64

And it passes!

Now we need to add in an authorized_keys file to this new directory, then populate it with our ssh key.

First, a test.

test/integration/user/serverspec/user_spec.rb

  describe file('/home/deploy/.ssh/authorized_keys') do
    it { should be_file }
    its(:content) { should match /ssh-rsa.+devops workshop key/ }
  end

Then, as usual, run the tests.

  vagrant@workshop $ kitchen verify user-ubuntu-14-04-x64

And watch them fail.

Now let's make them pass. We need a template with the SSH key in it.

Change directories back to your Chef repo directory

  vagrant@workshop $ cd ~/my_web_server_chef_repo

And generate the template.

  vagrant@workshop $ chef generate template cookbooks/my_web_server_cookbook ssh_key

Now change directories back to your cookbook directory:

  vagrant@workshop $ cd cookbooks/my_web_server_cookbook

Then open up the new template and add this content.

templates/default/ssh_key.erb

  [Paste in PUBLIC SSH Key provided by instructors]

Now add this into your recipes/user.rb file:

  template '/home/deploy/.ssh/authorized_keys' do
    source 'ssh_key.erb'
  end

And converge, then re-run the tests.

  vagrant@workshop $ kitchen converge user-ubuntu-14-04-x64
  vagrant@workshop $ kitchen verify user-ubuntu-14-04-x64

And they should pass!

Now let's apply these changes to our testing node. Run this command:

  vagrant@workshop $ knife solo cook root@[testing_node_ip_address]

Deploying again

Let's switch back to the widgetworld directory.

  vagrant@workshop $ cd ~/widgetworld

And try the deploy check again.

  vagrant@workshop $ cap staging deploy:check

If you receive git error, do this:

  vagrant@workshop $ ssh-add ~/.ssh/id_rsa

And it fails again. Let's take a look at that error:

  ERROR linked file /var/www/widgetworld/shared/config/database.yml does not exist

Capistrano expects to find a file that isn't there. Let's get this taken care of.

[For the sake of time, we're not adding this file into our Chef cookbook. However, extra credit points to any student who figures out how to!)

SSH into your test instance.

Notice that we're now ssh'ing in as the DEPLOY user.

  ssh deploy@[your staging instance]

Create and open this file on your VM.

  deploy@staging $ vim /var/www/widgetworld/shared/config/database.yml

And add this content:

# PostgreSQL. Versions 8.2 and up are supported.
#
# Install the pg driver:
#   gem install pg
# On OS X with Homebrew:
#   gem install pg -- --with-pg-config=/usr/local/bin/pg_config
# On OS X with MacPorts:
#   gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
# On Windows:
#   gem install pg
#       Choose the win32 build.
#       Install PostgreSQL and put its /bin directory on your path.
#
# Configure Using Gemfile
# gem 'pg'
#
default: &default
  adapter: postgresql
  encoding: unicode
# For details on connection pooling, see rails configuration guide
# http://guides.rubyonrails.org/configuring.html#database-pooling
pool: 5

development:
  <<: *default
  database: widgetworld_development

# The specified database role being used to connect to postgres.
# To create additional roles in postgres see `$ createuser --help`.
# When left blank, postgres will use the default role. This is
# the same name as the operating system user that initialized the database.
username: nellshamrell

# The password associated with the postgres role (username).
#password:

# Connect on a TCP socket. Omitted by default since the client uses a
# domain socket that doesn't need configuration. Windows does not have
# domain sockets, so uncomment these lines.
#host: localhost

# The TCP port the server listens on. Defaults to 5432.
# If your server runs on a different port number, change accordingly.
#port: 5432

# Schema search path. The server defaults to $user,public
#schema_search_path: myapp,sharedapp,public

# Minimum log levels, in increasing order:
#   debug5, debug4, debug3, debug2, debug1,
#   log, notice, warning, error, fatal, and panic
# Defaults to warning.
#min_messages: notice

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: widgetworld_test

# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
#   DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
#   production:
#     url: <%= ENV['DATABASE_URL'] %>
#

staging:
  <<: *default
  database: widgetworld_staging
  username: deploy
  password: <%= ENV['WIDGETWORLD_DATABASE_PASSWORD'] %>

production:
  <<: *default
  database: widgetworld_production
  username: deploy
  password: <%= ENV['WIDGETWORLD_DATABASE_PASSWORD'] %>

Did you notice that the password refers to an environment variable? This is because passwords can be dangerous to keep in source code. Now we need to set that environment variable.

Let's set that environment variable now. On your testing VM, run this command:

   vagrant@workshop $ export WIDGETWORLD_DATABASE_PASSWORD=[password you used in your chef recipe for the deploy user to login to postgres]

Then run the deploy check again.

  vagrant@workshop $ cap staging deploy:check

Whoops, another error, it's not finding a file it expects to be there.

  ERROR linked file /var/www/widgetworld/shared/config/secrets.yml does not exist on 45.55.153.64

Let's add that one in. Just add an empty file for now and we'll come back to this later.

SSH into your staging instance:

  vagrant@workshop $ ssh deploy@[your staging instance ip]

And create this file: (we'll add content shortly)

  deploy@staging $ touch /var/www/widgetworld/shared/config/secrets.yml

Now exit out of your staging instance and, from your development VM, run:

  vagrant@workshop $ cap staging deploy:check

If it does not return an error and exits with a line similar to:

  DEBUG [7f34dcb9] Finished in 0.004 seconds with exit status 0 (successful).

Then we're ready to deploy for real!

Deploying with Capistrano

Deploy with

  vagrant@workshop $ cap staging deploy

Whoops, another error:

  DEBUG [c880919a] Command: cd /var/www/widgetworld/releases/20150330230130 && /usr/local/rvm/bin/rvm default do bundle install --path /var/www/widgetworld/shared/bundle --without development test --deployment --quiet
  DEBUG [c880919a]        /usr/local/rvm/scripts/set: line 19: exec: bundle: not found

We need to install bundler on the VM (this is also something that we could capture in a Chef recipe)

SSH into your staging instance as the deploy user

  vagrant@workshop $ ssh deploy@[your staging instance ip]

Then run:

  deploy@staging $ sudo gem install bundler

Exit out of your staging instance, and run this command again

  vagrant@workshop $ cap staging deploy

And it looks like it worked!

The next thing we need to do is create our database. Capistrano does not currently allow you to do this through the tool, so we're going to do it manually.

  vagrant@workshop $ ssh deploy@[your staging instance ip]

Then change directories to your current widgetworld deploy:

  deploy@staging $ cd /var/www/widgetworld/current

Then create your database. (We're using a very common Ruby/Rails tool called Rake

  deploy@staging $ RAILS_ENV=staging rake db:create

If you get no output, that means it was a success!

Exit out of your staging instance.

Back on your VM, make sure you're in your widgetworld directory:

  vagrant@workshop $ cd ~/widgetworld

Now that the database is created, we're going to set up the tables for our database. Rails does this through database migrations. Fortunately, Capistrano can do this for use.

Open up your Capfile

  vagrant@workshop $ vim Capfile

And uncomment this line:

 # require 'capistrano/rails/migrations'

So it looks like this:

  require 'capistrano/rails/migrations'

Now run your deploy again to run these migrations:

  vagrant@workshop $ cap staging deploy

If you see this output, the migration was successful!

  DEBUG [48ce67f9]        == 20141223043443 CreateWidgets: migrating ====================================
  DEBUG [48ce67f9]        -- create_table(:widgets)
  DEBUG [48ce67f9]           -> 0.0042s
  DEBUG [48ce67f9]        == 20141223043443 CreateWidgets: migrated (0.0043s) ===========================

Now it did work...but we received this warning.

  DEBUG [48ce67f9]        config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
  DEBUG [48ce67f9]
  DEBUG [48ce67f9]          * development - set it to false
  DEBUG [48ce67f9]          * test - set it to false (unless you use a tool that preloads your test environment)
  DEBUG [48ce67f9]          * production - set it to true

Rails keeps different config files in the config/environments folders. There's not one for staging by default. Let's fix this:

SSH into your staging instance:

  vagrant@workshop $ ssh deploy@[your staging instance IP address]

Then change directories to your widgetworld directory

  vagrant@workshop $ cd /var/www/widgetworld

And run this command, we're going to give our staging instance the same eager_load config as our production instance.

  vagrant@workshop $ cp /var/www/widgetworld/current/config/environments/production.rb /var/www/widgetworld/current/config/environments/staging.rb

Now restart Apache to reload the rails application:

Now let's take a look at that IP address in your browser, will we see our newly running widgetworld application? Not quite...we still see our custom Apache homepage.

Making Apache aware of our site

Finally, we need to make Apache aware of our new site.

Make sure you're back in you Development VM, then change to your cookbook directory.

  vagrant@workshop $ cd ~/fresh_start_web_server_repo/cookbooks/my_web_server_cookbook

First, let's add a test to test/integration/app/serverspec/app.rb

  describe file('/etc/apache2/apache2.conf') do
    it { should be_file }
    its(:content) { should match /<VirtualHost \*:80>/ }
  end

Then run the test to watch it fail.

  vagrant@workshop $ kitchen verify app-ubuntu-14-04-x64

Now let's make it pass.

Modify your Apache config template. Add these lines to the file (no worries if hardcoding the IP makes you twitch, we'll change that shortly).

Now create a new Apache 2 template for use with the app recipe.

You need to run the generate template command from your my_web_server_chef_repo directory

  vagrant@workshop $ cd ~/fresh_start_web_server_repo
  vagrant@workshop $ chef generate template cookbooks/my_web_server_cookbook app-apache2.conf

Now change directories back to your cookbook directory:

  vagrant@workshop $ cd cookbooks/my_web_server_cookbook

Now open up your new template file and add this content.

templates/default/app-apache2.conf.erb

# This is the main Apache server configuration file.  It contains the
# configuration directives that give the server its instructions.
# See http://httpd.apache.org/docs/2.4/ for detailed information about
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
# hints.
#
#
# Summary of how the Apache 2 configuration works in Debian:
# The Apache 2 web server configuration in Debian is quite different to
# upstream's suggested way to configure the web server. This is because Debian's
# default Apache2 installation attempts to make adding and removing modules,
# virtual hosts, and extra configuration directives as flexible as possible, in
# order to make automating the changes and administering the server as easy as
# possible.

# It is split into several files forming the configuration hierarchy outlined
# below, all located in the /etc/apache2/ directory:
#
#       /etc/apache2/
#       |-- apache2.conf
#       |       `--  ports.conf
#       |-- mods-enabled
#       |       |-- *.load
#       |       `-- *.conf
#       |-- conf-enabled
#       |       `-- *.conf
#       `-- sites-enabled
#               `-- *.conf
#
#
# * apache2.conf is the main configuration file (this file). It puts the pieces
#   together by including all remaining configuration files when starting up the
#   web server.
#
# * ports.conf is always included from the main configuration file. It is
#   supposed to determine listening ports for incoming connections which can be
#   customized anytime.
#
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
#   directories contain particular configuration snippets which manage modules,
#   global configuration fragments, or virtual host configurations,
#   respectively.
#
#   They are activated by symlinking available configuration files from their
#   respective *-available/ counterparts. These should be managed by using our
#   helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
#   their respective man pages for detailed information.
#
# * The binary is called apache2. Due to the use of environment variables, in
#   the default configuration, apache2 needs to be started/stopped with
#   /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
#   work with the default configuration.


# Global configuration
#

#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# NOTE!  If you intend to place this on an NFS (or otherwise network)
# mounted filesystem then please read the Mutex documentation (available
# at <URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex>);
# you will save yourself a lot of trouble.
#
# Do NOT add a slash at the end of the directory path.
#
#ServerRoot "/etc/apache2"

#
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
#
Mutex file:${APACHE_LOCK_DIR} default

#
# PidFile: The file in which the server should record its process
# identification number when it starts.
# This needs to be set in /etc/apache2/envvars
#
PidFile ${APACHE_PID_FILE}

#
# Timeout: The number of seconds before receives and sends time out.
#
Timeout 300

#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive On

#
# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
#
MaxKeepAliveRequests 100

#
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 5


# These need to be set in /etc/apache2/envvars
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}

#
# HostnameLookups: Log the names of clients or just their IP addresses
# e.g., www.apache.org (on) or 204.62.129.132 (off).
# The default is off because it'd be overall better for the net if people
# had to knowingly turn this feature on, since enabling it means that
# each client request will result in AT LEAST one lookup request to the
# nameserver.
#
HostnameLookups Off

# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here.  If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog ${APACHE_LOG_DIR}/error.log

#
# LogLevel: Control the severity of messages logged to the error_log.
# Available values: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the log level for particular modules, e.g.
# "LogLevel info ssl:warn"
#
LogLevel warn

# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf

# Include list of ports to listen on
Include ports.conf


# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
        Options FollowSymLinks
        AllowOverride None
        Require all denied
</Directory>

<Directory /usr/share>
        AllowOverride None
        Require all granted
</Directory>

<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
</Directory>

#<Directory /srv/>
#       Options Indexes FollowSymLinks
#       AllowOverride None
#       Require all granted
#</Directory>




# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives.  See also the AllowOverride
# directive.
#




# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives.  See also the AllowOverride
# directive.
#
AccessFileName .htaccess

#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<FilesMatch "^\.ht">
        Require all denied
</FilesMatch>


#
# The following directives define some format nicknames for use with
# a CustomLog directive.
#
# These deviate from the Common Log Format definitions in that they use %O
# (the actual bytes sent including headers) instead of %b (the size of the
# requested file), because the latter makes it impossible to detect partial
# requests.
#
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
# Use mod_remoteip instead.
#
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

# Include of directories ignores editors' and dpkg's backup files,
# see README.Debian for details.

# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf

# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf

LoadModule passenger_module /var/lib/gems/1.9.1/gems/passenger-5.0.5/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
  PassengerRoot /var/lib/gems/1.9.1/gems/passenger-5.0.5
  PassengerDefaultRuby /usr/bin/ruby1.9.1
</IfModule>

<VirtualHost *:80>
ServerName [Your staging server IP]
DocumentRoot /var/www/widgetworld/current/public
<Directory /var/www/widgetworld/current/public>
# This relaxes Apache security settings.
AllowOverride all
# MultiViews must be turned off.
Options -MultiViews
</Directory>
</VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

Then add this to your recipe/default.rb file

recipes/app.rb

  template '/etc/apache2/apache2.conf' do
    source 'app-apache2.conf.erb'
  end

Now converge

  vagrant@workshop $ kitchen converge app-ubuntu-14-04-x64

And verify that the tests pass:

  vagrant@workshop $ kitchen verify app-ubuntu-14-04-x64

Huzzah!

Make sure to apply these changes to your testing instance.

  vagrant@workshop $ knife solo cook root@[testing_node_ip_address]

Now let's look at the IP of your staging instance in your favorite browser:

What a minute...we still see that custom page. What gives?

Turns out, anytime we add a new site to our Apache config, we need to restart the web server. Let's do that now, adding it to our app recipe.

recipes/app.rb

  service 'apache2' do
    action [:restart]
  end

Now refresh the page in your browser:

Another error??? At least it's a different one, this time.

  Incomplete response received from application

Well...that's not exactly helpful. Let's do some troubleshooting. SSH into your staging VM:

  ssh deploy@[your staging instance IP address]

A great place to look for errors when you're running Apache is the Apache logs. Let's tail this log (meaning we will watch the log be generated in real time).

  deploy@staging $ sudo tail -f /var/log/error.log

And let's reload the page in our browser, then take a look back at the console.

Aha! There's the culprit!

  App 30307 stderr: [ 2015-03-30 16:47:25.7163 30388/0x000000020823a0(Worker 1) utils.rb:85 ]: *** Exception RuntimeError in Rack application object (Missing `secret_key_base` for 'production' environment, set this value in `config/secrets.yml`) (process 30388, thread 0x000000020823a0(Worker 1)):

Remember that secrets.yml file we created? Now we we need to actually populate it with something.

Configuring the secrets environmental variable

There's one more environmental variable we need to configure. Rails 4 introduced the concept of a secrets file. For more info, check out this blog post.

Still on your staging instance, open up the /var/www/widgetworld/shared/config/secrets.yml file and add this content:

/var/www/widgetworld/shared/config/secrets.yml

  # Be sure to restart your server when you modify this file.

  # Your secret key is used for verifying the integrity of signed cookies.
  # If you change this key, all old signed cookies will become invalid!

  # Make sure the secret is at least 30 characters and all random,
  # no regular words or you'll be exposed to dictionary attacks.
  # You can use `rake secret` to generate a secure secret key.

  # Make sure the secrets in this file are kept private
  # if you're sharing your code publicly.

  development:
    secret_key_base: 7ddf23906edc95aed228a016bf57ceda80901d9b0a8d58a5798a54fa5aa4e8509425ff7df5d63f65a393cc27f1ebd4820ff18d5c9d3d753d38037bb9287d9037

  test:
    secret_key_base: 5c565a66bd249c306d5b399086b238fe506fba9a7dc1779459fcc2239fd784b8fa156f64fbb3a0b7ed82bc4fb302bd6fc7dec79976ffc5c9abde95bd21c5a3ba

  # Do not keep production secrets in the repository,
  # instead read values from the environment.
    production:
      secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

See this content at the bottom?

  # Do not keep production secrets in the repository,
  # instead read values from the environment.
    production:
      secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

We need to add another couple of lines to this file to use it in a staging environment. Alter the last part of the file so it looks like this:

  # Do not keep production secrets in the repository,
  # instead read values from the environment.
    production:
      secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
    staging:
      secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

We need to configure that environmental variable. For the sake of time, we're going to do this one manually, though it would be ideal to incorporate this into our Chef recipe.

Change directories into your current widgetworld directory.

  deploy@staging $ cd /var/www/widgetworld/current

Then run this command to generate your secret key base:

  deploy@staging $ RAILS_ENV=staging rake secret

Now open up the deploy user's bash profile:

  deploy@staging $ vim ~/.bash_profile

And add this content:

  export SECRET_KEY_BASE=[code you just generated]

You can then make sure this variable is set correctly by running

  deploy@staging $ echo $SECRET_KEY_BASE

Then source the bash profile:

  deploy@staging $ source ~/.bash_profile

Now restart Apache to make sure Rails loads this environmental variable:

  deploy@staging $ sudo service apache2 restart

And you should see a page that's ready to list some widgets!