Capistrano Prompt For Server Selection

Problem #

At the time I was working on a project that had two application servers on AWS EC2 running behind an EC2 load balancer. For a reason I never uncovered when I would deploy to one of the application servers the application would become unavailable for a period of time even though the other application server was not interrupted. Therefore the process to deploy the project was to deploy to one instance at a time, modifying the Capistrano recipe at least once for every release. This got old pretty fast. What I wanted was to get prompted with a numbered list of servers upon deployment, enter the corresponding number and have Capistrano deploy to that server only. Then I could swap the servers out on the EC2 load balancer, and run it again this time picking the other server.

The Solution #

I looked at a few gems that appeared to provide the desired functionality by extending Capistrano, but none of them really seemed to fit well and I wasn’t interested in redesigning the project’s deployment scheme or adding a gem for such a simple task for that matter.

With no real desire to get fancy by querying AWS for the servers I decided I could accomplish my goal with not much more than a Hash for prompt and selection management. In fact I could probably reduce the complexity by using an Array. I made a compromise between automation, stability and minimal effort.

Here is what I added to the project’s config/deploy.rb

set :servers, {'1' => 'server1', '2' => 'server2'}

def get_server_choice
  return fetch(:server_choice) if exists?(:server_choice)
  puts "Please choose one of the following servers to deploy to..."
  fetch(:servers).each {|key, name| puts "#{key}: #{name}"}
  choice = Capistrano::CLI.ui.ask("Enter number (or Cancel): ")
  if choice != 'Cancel' && fetch(:servers).keys.include?(choice)
    set :server_choice, fetch(:servers)[choice]
    return fetch(:server_choice)
    raise "User cancelled deployment or entered an invalid server choice."

role(:app) do

role(:web) do

role(:db) do
  [get_server_choice, {:primary => true}]

Perhaps some refactoring could take place in the get_server_choice method. I initially tried using a Capistrano callback but experienced major breakdown as a result.

This solution could be expanded to query the AWS EC2 API to present the list of servers, for small setups (1-3 servers) this works pretty well. Once the project grew beyond just a few servers I found the application would survive when deploying to say 4 of 8 application servers. Originally I intended to automate the entire deployment process including release to both servers one at a time, no prompts at all. While this is certainly possible, it carries some complications that were not worth solving for the project.

The Takeaway #

Sometimes it does make sense to roll your own solution, I felt this was one of those scenarios. Building your own WYSIWYG is probably an awful idea, no…it is an awful idea. Always spend a few minutes looking for an existing solution. Just don’t be afraid to build your own when you feel that you can in a fraction of the time to integrate another solution.

Please comment and tell me what you think about this or any suggestions for refactor.


Now read this

Minimum MySQL Privileges for Rails

Every time I go to setup a dedicated user in a MySQL database server to support a Rails application I find myself searching the web for the privileges that Rails needs. By the way, if you are hooking to a MySQL database in your Rails... Continue →