28
Nov
2012

Capistrano
Deployment

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.

15
Jun
2012

MySQL
Security
Ruby on Rails

Minimum Required 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 application and using the root account to connect the app to the database, you are doing it wrong.

Below are the minimal necessary privileges needed by Rails to do its thing:

  • Select
  • Insert
  • Update
  • Delete
  • Create
  • Drop
  • Index
  • Alter
  • Lock Tables

If your Rails application does anything out of the ordinary additional privileges may be necessary.

Below is how you might setup a user on your MySQL server from the MySQL command line.

CREATE DATABASE cutepuppies_production;
CREATE USER 'cutepuppies'@'localhost';
SET PASSWORD FOR 'cutepuppies'@'localhost' = PASSWORD('poopingonyourcarpet');
GRANT Select,Insert,Update,Delete,Create,Drop,Index,Alter,Lock Tables ON cutepuppies_production.* TO 'cutepuppies'@'localhost';
FLUSH PRIVILEGES;

If your database is running on a separate server from your application (and hopefully it is) you should substitute ‘localhost’ for the IP or hostname of your application server(s). If you have many application servers it might be worth the security risk to just use a wildcard (%) for the hostname part, see the MySQL documentation for more info.

15
Feb
2012

ActiveAdmin
Ruby on Rails
Mongoid
NoSQL

Getting ActiveAdmin To Work With Mongoid On Rails 3.2.x

I am working on an app where I wanted to use ActiveAdmin. The interesting part is we are using Mongo for the database and chose Mongoid as the ORM. We aren’t budging on that decision and ActiveAdmin is not natively ORM agnostic. I found this Github Repository that has an example app where neccesary patching is done to allow ActiveAdmin and Mongoid to not just coexist but work cohesively to deliver an admin interface. Based on our Git history and my memory (which is rarely accurate) the most important part is the patch in initializers. There may have also been some tweaks made in registering models in app/admin.

Recently to easier merge with a hosting environment as well as keep the app fresh and up to date, we put the app on Rack 1.4.1 and Rails 3.2.x. This required upgrading meta_search gem and activeadmin gem. Afterwards some of the patching that had been done started whipping nasty errors around.

superclass mismatch for class ResourceController (TypeError)

At this point our config/initializers/active_admin_mongoid_patches.rb looked like:

module ActiveAdmin
  class Namespace
    # Disable comments
    def comments?
      false
    end
  end

  class Resource
    def resource_table_name
      resource.collection_name
    end

    # Disable filters
    def add_default_sidebar_sections
    end
  end

  class ResourceController < ::InheritedResources::Base
    # Use #desc and #asc for sorting.
    def sort_order(chain)
      params[:order] ||= active_admin_config.sort_order
      table_name = active_admin_config.resource_table_name
      if params[:order] && params[:order] =~ /^([\w\_\.]+)_(desc|asc)$/
        chain.send($2, $1)
      else
        chain # just return the chain
      end
    end

    # Disable filters
    def search(chain)
      chain
    end
  end
end

Fixing our little dilemma is really quite simple, our superclass exception is occurring because we are re-opening the ActiveAdmin::ResourceController class, but the original one as it is defined does not inherit from ::InheritedResources::Base (anymore) instead it now inherits from ActiveAdmin::Base. So lets fix that.

module ActiveAdmin
  class Namespace
    # Disable comments
    def comments?
      false
    end
  end

  class Resource
    def resource_table_name
      resource.collection_name
    end

    # Disable filters
    def add_default_sidebar_sections
    end
  end

  class ResourceController < ActiveAdmin::BaseController
    # Use #desc and #asc for sorting.
    def sort_order(chain)
      params[:order] ||= active_admin_config.sort_order
      table_name = active_admin_config.resource_table_name
      if params[:order] && params[:order] =~ /^([\w\_\.]+)_(desc|asc)$/
        chain.send($2, $1)
      else
        chain # just return the chain
      end
    end

    # Disable filters
    def search(chain)
      chain
    end
  end
end

Great now everyone is happy!

30
Jan
2012

Ruby

Invalid Gemspec Warnings What RubyGems Left Behind

I have been going nuts over RubyGem warnings about invalid gemspec files since upgrading RubyGems. I finally had enough tonight, so I dug my feet in and prepared to get dirty. Turns out none of the gems that were actually installed to the RVM gemsets I was enountering this phenomena in were causing the warnings. Apparently RubyGems forgot to uninstall the gemspec files of old versions of gems I had installed. RubyGems must have still been picking them up when doing various gem management commands.

The warnings I was getting were something like this:

Invalid gemspec in
[/my_ultra_secret_path/specifications/coffee-script-source-1.1.3.gemspec]:
invalid date format in specification: "2011-11-08
00:00:00.000000000Z"

My solution? Simply rm -f those suckers. Wipe my hands clean and on I go. If you take the same approach I strongly urge you to ensure that the gem you are removing the gemspec file for IS NOT installed. If it is installed you should consider upgrading the version you are using or removing the gem if it is not needed. I would add that although the warnings are pest-like, they are probably not worth the risk of upgrading even only a minor version up of any specific gem. As usual proceed with caution, and at your own risk.