Heroku, MongoHQ, and Amazon S3 Backups

I spent a few hours yesterday convincing MongoHQ to back up a Mongo database to an Amazon S3 bucket via an Amazon “IAM” account. Here’s the secret recipe. Hopefully it saves someone time.

Context: MongoHQ, which I’m using with one of my work projects right now, is a service that hosts MongoDB databases. It integrates with Heroku, allowing you to get started on a Heroku app using MongoDB very quickly. MongoHQ supports hourly and daily backup of their databases to Amazon S3 buckets, but alas, not yet for databases provisioned automatically via Heroku. So if you want to step up to a backed-up database you’ll have to set up your own. Here are the steps I followed to do it.

Secret decoder ring:
S3 = Amazon Web Services Management Console, S3 tab.
IAM = Amazon Web Services Management Console, IAM tab.
MHQ = Mongo HQ console.
HKU = Heroku.

Recipe:

  1. S3 : Create a bucket for your app’s backups, e.g. “my-app-backups”.
  2. IAM: Create an IAM User account for MongoHQ to use, e.g. “mongohq”. Download the credentials and store them safely.
  3. IAM: Create an IAM Group to contain all users with backup permission, e.g. “myapp-backup-writers”.
  4. IAM: Add the mongohq User account to the newly created Group.
  5. IAM: Using the Permissions tab of the Group’s properties attach a new security policy, granting permission to write to the myapp-backups bucket. See below for a sample policy.
  6. MHQ: Sign up for your own MongoHQ account.
  7. MHQ: Create the database you wish to use with Heroku.
  8. MHQ: Select the “Backups” tab and enter the IAM User credentials from step 2.
  9. MHQ: Click “Save Settings”. If all is well you’ll see a message, “Setting Updated”.*
  10. MHQ: Create a new user on the Database Users tab.
  11. MHQ: Copy down the Mongo URI string from the Database Info tab, substituting your user name and password from step 10.
  12. HKU: In the root directory of your Heroku app do “heroku config:add MONGOHQ_URL=’URI’”. URI is the Mongo URI from step 11. (Normally Heroku sets this variable for you when it provisions a MongoHQ database. You’re overriding it with a link to your own database.)

* Step 9 is where I got stuck. It worked fine with my own personal security credentials, but not with the IAM account I created specifically for MongoHQ to use. (I wanted more security… giving your main account credentials to a 3rd party site isn’t smart.) I fiddled around with the security policy for quite a while, and eventually discovered I needed the “ListAllMyBuckets” permission. Here’s the security policy that worked for me:

    {
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [ "s3:ListAllMyBuckets" ],
          "Resource": "arn:aws:s3:::*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "s3:PutObject",
            "s3:PutObjectAcl"
          ],
          "Resource": [
            "arn:aws:s3:::MY-APP-BACKUPS/*"
          ]
        }
      ]
    }

ActionController::RoutingError (undefined method ‘sub’ for nil:NilClass)

Or, “Man I’m glad Ruby on Rails is an open source framework”.

Ruby on Rails normally provides pretty good debugging information for dealing with errors, but I ran into an odd error today that had no debug info other than the exception itself. Here’s what I did to find and fix the problem.

Symptoms: “So, tell me, how long have you been feeling this way?” I had just added a new controller, model, views, and helpers to my app. Requesting a route to any of the new controller’s actions caused Rails to die with the exception “ActionController::RoutingError (undefined method `sub’ for nil:NilClass)”. No stack trace. No filename / line numbers. I knew one of the 20-odd new files I had just created must be causing the problem, but I didn’t know which one.

Diagnosis: “Breathe in. Breathe again. Again. OK, now cough. Ah, yes, now I see the problem.”: The only way I know of to diagnose these kind of problems is brute force. I added a debugger statement to the top of application_controller.rb and restarted the app. Then I requested a URL that was causing crashes, and from the breakpoint, stepped forward in the debugger (next, next, next…) until the app died. Studying the code that executed just before the app died didn’t yield an obvious result, but about 10 frames up the stack I could see a call to ActiveSupport::Inflector#constantize. I edited that method to print (puts) the parameters it was being called with, then restarted the server and tried again. Voila: the last parameter spit out by #constantize before the server died was the culprit.

#constantize PlaceLinksHelper for name PlaceLinksHelper

The Cure: “Take three of these and call me in the morning.” : It turns out I had created a helper module with the file name “places_helper.rb”, but within that file I mistakenly defined “module PlaceLinksHelper”. For some reason that shall remain a mystery to me, this caused things to fall-down-go-boom. The fix was simply to give the module the name Rails expected to find: “PlacesHelper”.

Upon Reflection… “I sure hope that doesn’t happen to me again. But it probably will.” I don’t know how you do this sort of diagnosis efficiently in a closed-source or compiled environment.  This is about the 5th time I’ve edited Rails source code to find and fix an oddball problem. I guess you could try attaching a debugger to binaries and then study the gobbledy-gook, but… no thanks. The only other thing I  could have done in a compiled/closed source setup is study each of my own files line by line. That would have worked, but it would have taken far more time.

Source code good.

Follow

Get every new post delivered to your Inbox.

Join 348 other followers