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)
  else
    raise StandardError.new "User cancelled deployment or entered an invalid server choice."
  end
end

role(:app) do
  get_server_choice
end

role(:web) do
  get_server_choice
end

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

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.

 
1
Kudos
 
1
Kudos

Now read this

Installing wkhtmltopdf on MBP OSX Mavericks 10.9

After spending most of my work day evening installing wkhtmltopdf on my MBP I feel it necessary to at least document my decisions and what actually worked. I try to use Homebrew for everything I possibly can that does not come with and... Continue →