Globals are evil

And hard-coded strings are the tools of the devil.

On my NAS I had implemented a simple script to alert me of failures on
various scripts/backup jobs, using the Mandrill API and that had worked
just fine for quite a while.

However, it recently had started failing with the following tracelog:

01/01/2015 19:59:08 [INFO] Starting new HTTPS connection (1): mandrillapp.com
Traceback (most recent call last):
  [...]
  File "/share/MD0_DATA/.qpkg/Python/lib/python2.7/site-packages/requests/adapters.py", line 431, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: [Errno 1] _ssl.c:499: error:0D0890A1:asn1 encoding routines:ASN1_verify:unknown message digest algorithm

This was clearly due to some upgrades in the OpenSSL package that I was unable to update on my QNAP, as it depends on their firmware update schedule (which is generally pretty awesome, but for my now three-year-old model is becoming less frequent than it used to) – not to mention, there is no apt-get, aptitudeyum or any of their friends.

On my Ubuntu (14.04) box, the same code works just fine.
Searching on Google, Stack Overflow and similar sites, equally, yielded no obvious workaround.

Doing some further digging, the offending call is (credit to the Mandrill folks, they published their Python code on bitbucket):

r = self.session.post('%s%s.json' % (ROOT, url), data=params,
    headers={'content-type': 'application/json',
             'user-agent': 'Mandrill-Python/1.0.57'})

(I’ve prettified the code a bit and, yes, they could have more respect for PEP8, but that’s another story).

Immediately, that ROOT constant caught my attention and, lo and behold, on line 70 we have:

ROOT = 'https://mandrillapp.com/api/1.0/'

It was obvious that the issue was caused by using https: absolutely best practice, but, alas, causing the breakage in my case.

Unfortunately, there was no available ‘flag’ for me to toggle and force a plain http connection, for example when creating the instance of the Mandrill class:

# in alert.py
mc = mandrill.Mandrill(apikey=kwargs.get('api_key'), insecure=True)

as the https scheme is hard-coded in a global constant.

The only “workaround” (aka, a “dirty hack”) was for me to go into the site-packages folder in my NAS [1] , and directly edit the ROOT value to read:

ROOT = 'http://mandrillapp.com/api/1.0/'

The simple one-character change made the issue go away, but left me once more feel a deep hatred for hard-coded strings in code.

Morale of the story

  • do not use hard-coded strings in your code for things that clearly belong in a configuration file and/or command-line argument;
  • do not use global constants where (configurable) class variables would offer more flexibility;
  • always allow your code’s client the option of choosing/configuring things such as URI scheme, location of files, media types, etc. via optional constructor arguments or getters/setters.

Notes

[1] In case you have the same problem and need to follow along: /share/MD0_DATA/.qpkg/Python/lib/python2.7/site-packages/mandrill.py
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s