Repository: inhumantsar/python-ec2-reaper

Issue:
lambda return val isn't json serializable
datetimes cause lambda's return to fail. everything else still happens but the return code is always an error. 

Discussion:

Patch:
diff --git a/ec2_reaper/aws_lambda.py b/ec2_reaper/aws_lambda.py
index e9fcdd2..ff4f30b 100644
--- a/ec2_reaper/aws_lambda.py
+++ b/ec2_reaper/aws_lambda.py
@@ -25,10 +25,15 @@ TAG_MATCHER = json.loads(TAG_MATCHER) if isinstance(TAG_MATCHER, strclasses) els
 SLACK_ENDPOINT = os.environ.get('SLACK_ENDPOINT', None)
 
 DEBUG = os.environ.get('DEBUG', True)
+log.debug('startup: got value for DEBUG: {} ({})'.format(DEBUG, type(DEBUG)))
+if isinstance(DEBUG, str):
+    DEBUG = False if DEBUG.lower() == 'false' else True
+
 if DEBUG:
     log.setLevel(logging.DEBUG)
     logging.getLogger('botocore').setLevel(logging.INFO)
     logging.getLogger('boto3').setLevel(logging.INFO)
+    log.debug('startup: debug logging on')
 else:
     log.setLevel(logging.INFO)
     logging.getLogger('botocore').setLevel(logging.WARNING)
@@ -42,9 +47,12 @@ class DateTimeJSONEncoder(json.JSONEncoder):
         return json.JSONEncoder.default(self, o)
 
 def _respond(body, error=True, headers=None, status_code=500):
-    o = {'statusCode': status_code, 'body': body}
+    o = {'statusCode': status_code}
     if headers:
         o['headers'] = headers
+
+    # just in case body contains untranslatable datetimes
+    o['body'] = json.loads(json.dumps(body, cls=DateTimeJSONEncoder))
     return o
 
 def _get_expires(launch_time, min_age=MIN_AGE):


Test patch:
diff --git a/tests/test_lambda_handler.py b/tests/test_lambda_handler.py
index bc153b4..298f793 100644
--- a/tests/test_lambda_handler.py
+++ b/tests/test_lambda_handler.py
@@ -2,6 +2,7 @@ import logging
 import json
 import sys
 from datetime import datetime, timedelta
+import os
 
 from ec2_reaper import aws_lambda
 from ec2_reaper import LOCAL_TZ
@@ -10,12 +11,19 @@ logging.basicConfig(level=logging.DEBUG)
 logging.getLogger('botocore').setLevel(logging.INFO)
 logging.getLogger('boto3').setLevel(logging.INFO)
 
+# mock has some weirdness in python 3.3, 3.5, and 3.6
 if sys.version_info < (3, 0) or (sys.version_info >= (3, 5) and
                                  sys.version_info < (3, 6)):
     from mock import patch
 else:
     from unittest.mock import patch
 
+# py 2.7 has reload built in but it's moved around a bit in py3+
+if sys.version_info >= (3, 0) and sys.version_info < (3, 4):
+    from imp import reload
+elif sys.version_info >= (3, 4):
+    from importlib import reload
+
 # when no results, handler should have called reap, *not* called (slack) notify,
 # and should have returned a happy response json obj,
 @patch.object(aws_lambda, 'reap')
@@ -53,5 +61,29 @@ def test_reap_2neg_1pos(mock_notify, mock_reap):
     mock_notify.assert_called()
     mock_reap.assert_called_once()
     assert r['statusCode'] == 200
-    assert r['body']['log'] == mock_reap_results
+    assert r['body']['log'] == json.loads(json.dumps(mock_reap_results, cls=aws_lambda.DateTimeJSONEncoder))
     assert r['body']['reaped'] == 1
+
+# env vars come in as strings, so bools like DEBUG need testing
+def test_debug_envvar():
+    from ec2_reaper import aws_lambda as al
+    # true
+    os.environ['DEBUG'] = 'true'
+    reload(al)
+    assert al.DEBUG == True
+    os.environ['DEBUG'] = 'True'
+    reload(al)
+    assert al.DEBUG == True
+
+    # default to safety
+    os.environ['DEBUG'] = 'mooooooooo'
+    reload(al)
+    assert al.DEBUG == True
+
+    # false
+    os.environ['DEBUG'] = 'False'
+    reload(al)
+    assert al.DEBUG == False
+    os.environ['DEBUG'] = 'false'
+    reload(al)
+    assert al.DEBUG == False