hit counter

Timeline

My development logbook

Authenticate Mandrill Webhook Call in Python

There are plenty of examples in php out there, but I cannot find any in python. I reckon I can share my implementation here.

It is assuming you are using Google App Engine

    import webapp2
    import hashlib
    import hmac

    class MandrillWebhookHandler(webapp2.RequestHandler):

        def _calc_signature(self, raw, key):
            hashed = hmac.new(key, raw, hashlib.sha1)
            return hashed.digest().encode("base64").rstrip('\n')

        def verify_mandrill_signature(self):
            '''
            Mandrill includes an additional HTTP header with webhook POST requests,
                X-Mandrill-Signature, which will contain the signature for the request.
                To verify a webhook request, generate a signature using the same key
                that Mandrill uses and compare that to the value of the
                X-Mandrill-Signature header.
            :return: True if verified valid
            '''
            mandrill_signature = self.request.headers['X-Mandrill-Signature']
            mandrill_key = 'Your mandrill webhook key goes here'
            signed_data = self.request.path_url
            sorted_key = sorted(self.request.arguments())
            for k in sorted_key:
                signed_data += k
                signed_data += self.request.get(k)
            expected_signature = self._calc_signature(signed_data, mandrill_key)
            return expected_signature == mandrill_signature

        def head(self):
            '''
            Must return 200 so Mandrill knows it is a valid webhook

            http://help.mandrill.com/entries/22024856-Why-can-t-my-webhook-or-inbound-route-URL-be-verified-
            '''
            self.response.set_status(OK)

        def post(self):
            '''
            Mandrill sends data to our webhook by post
            '''
            if not self.verify_mandrill_signature():
                # check failed
                self.abort()

            # The rest of processing
            # ...

Update ChefDK in OSX

A new version of ChefDK 0.4.0 has been released. I did an upgrade via brew cask

    $ brew update
    ...
    $ brew cask install --force chefdk
    ==> Downloading https://opscode-omnibus-packages.s3.amazonaws.com/mac_os_x/10.8/x86_64/chefdk-0.4.0-1.dmg
    ######################################################################## 100.0%
    ==> Running installer for chefdk; your password may be necessary.
    ==> Package installers may write to any location; options such as --appdir are ignored.
    Password:
    ==> installer: Package name is Chef Development Kit
    ==> installer: Upgrading at base path /
    ==> installer: The upgrade was successful.
    🍺  chefdk staged at '/opt/homebrew-cask/Caskroom/chefdk/0.4.0-1' (6 files, 99M)

A Little Gotcha With @patch

This is my code

from mail_api import send_mail

def my_func():
    # do some work
    send_mail(to="test@example.com", content="data)
    # do some more work
    return result

Here is my unit test. I want to test for the return value but I do not want to send out any email at all from the unit test. That’s why I patch the send_mail function.

from mock import patch

  class MyTest(unittest.TestCase):

    @patch('mail_api.send_mail')
    def test_my_func(self, send_mail):
      self.assertEquals(my_func(), expected_result)

To my surprise an email is still sent out when the test is run!

It turns out my_func still retains a reference to the original send_mail function. If I want to successfully patch the mail api, I need to rewrite my_func like this:

import mail_api

def my_func():
    # do some work
    mail_api.send_mail(to="test@example.com", content="data)
    # do some more work
    return result

Chef, Brekshelf and Data Bag

After some trial and errors, the best way (as of this writing) to manage cookbooks for vagrant + chef is to use brekshef. You can forget about knife or librarian-chef.

If you use berkshelf with vagrant, you will need to install the vagrant-berkshelf plugin. The plugin is quite clever and you do not need to specify the cookbook path for your chef-solo or chef-zero provisioner.

However it does not apply to data_bags. You will still need to specify the path to data_bags if you need to use data bag.