Selecting A Server At Runtime With Capistrano
I was getting tired of modifying my Capistrano configuration twice to do a single deploy, we had two EC2 instances in a load balancer and would deploy to one at a time. I looked at a few gems that appeared to add similar functionality by extending Capistrano, but they we’re either cumbersome, not quite what I wanted or felt a little heavy handed. I didn’t want to have to rethink our entire deployment recipe. I thought about it for a second and realized all I want to do is run ‘cap production deploy’ and get a prompt to pick a server, pick my server and continue on as normal. Then swap the servers out on the load balancer, and run it again this time picking another app. It is very, very simple, and adding another gem to our Bundle is definitely overkill.
Here is what I added to our 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
I’m sure there is some refactoring and better ways to accomplish some of what happens in get_server_choice. I tried using a callback in the beginning but there we’re serious problems with that.
Eventually I plan on evolving this to querying AWS EC2 API and presenting a list of servers, but until this app really has to do some scaling this works pretty well. There was also an original intention of having the entire deployment process automated, including deploying to both servers one at a time, no selections at all. While this is certainly possible, it carries a lot of complications many that are best suited for human interaction. Making sure the app comes up before adding into the load balancer and pulling another one out. This can be programmed with enough inspiration but for now we just do not need that.
Please comment and tell me what you think about this or any suggestions for refactor you have.