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.

 
2
Kudos
 
2
Kudos

Now read this

Benchmarking RandomX on Mac

I wanted to test my MBP CPU out on the incoming Monero algorithm RandomX but there are no builds for Mac and I wasn’t in the mood to try and compile from source on Mac. Docker to the rescue! Install Docker Desktop... Continue →