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, aptitude, yum 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 |
Leave a Reply