Skip to content

Commit 8e6c53a

Browse files
authored
Merge pull request #2 from cevoaustralia/add-tests
Enabled TravisCI and fixed SNS issue
2 parents 63a73fc + 63f076a commit 8e6c53a

File tree

8 files changed

+266
-20
lines changed

8 files changed

+266
-20
lines changed

.travis.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
language: python
2+
python:
3+
- "2.6"
4+
- "2.7"
5+
# - "3.2"
6+
# - "3.3"
7+
# - "3.4"
8+
# - "3.5"
9+
# - "3.5-dev" # 3.5 development branch
10+
# - "3.6"
11+
# - "3.6-dev" # 3.6 development branch
12+
# - "3.7-dev" # 3.7 development branch
13+
# - "nightly" # currently points to 3.7-dev
14+
# command to install dependencies
15+
install:
16+
- "pip install -r requirements.txt"
17+
- "pip install -r development.txt"
18+
env:
19+
- PYTHONPATH=lambda
20+
# command to run tests
21+
script: nosetests --with-xunit --with-coverage --cover-package=lambda --cover-erase
22+
23+
notifications:
24+
slack:
25+
on_success: always
26+
on_failure: always

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
aws-backup-lambda
22
=================
33

4+
[![Build Status](https://travis-ci.org/cevoaustralia/aws-backup-lambda.svg?branch=master)](https://travis-ci.org/cevoaustralia/aws-backup-lambda)
5+
46
A utility AWS lambda function to manage EBS and RDS snapshot backups.
57

68
The Lambda function takes new backups when executed, and manages the deletion of the old ones when the upper limit is reached.

cloudformation.yaml

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
AWSTemplateFormatVersion: '2010-09-09'
22
Transform: AWS::Serverless-2016-10-31
33
Description: A simple backup process for EC2 and RDS datastores, tag your resources with 'MakeSnapshot' = 'True' (casesensitive) to have snapshots taken and managed
4+
Parameters:
5+
SuccessSNSTopicOption:
6+
Description: Supply an either '' (disabled), 'CreateSNS' (build new SNS for this stack), '<sns_arn>' (connect to existing Topic).
7+
Default: ""
8+
Type: String
9+
ErrorSNSTopicOption:
10+
Description: Supply an either '' (disabled), 'CreateSNS' (build new SNS for this stack), '<sns_arn>' (connect to existing Topic).
11+
Default: ""
12+
Type: String
13+
14+
Conditions:
15+
EnableSuccessSNSTopic: !Not [!Equals [!Ref SuccessSNSTopicOption, ""]] #
16+
EnableErrorSNSTopic: !Not [!Equals [!Ref ErrorSNSTopicOption, ""]]
17+
18+
CreateSuccessSNSTopic: !And # We only need to create the Success SNS when it is enabled and not supplied
19+
- Condition: EnableSuccessSNSTopic
20+
- !Equals [!Ref SuccessSNSTopicOption, "CreateSNS"]
21+
22+
CreateErrorSNSTopic: !And # We only need to create the Error SNS when it is enabled and not supplied
23+
- Condition: EnableErrorSNSTopic
24+
- !Equals [!Ref ErrorSNSTopicOption, "CreateSNS"]
25+
426
Resources:
527
LambdaExecutionRole:
628
Type: "AWS::IAM::Role"
@@ -60,7 +82,12 @@ Resources:
6082
- "rds:CreateDBSnapshot"
6183
- "rds:DeleteDBSnapshot"
6284
Resource: "*"
63-
85+
SuccessSNSTopic:
86+
Type: "AWS::SNS::Topic"
87+
Condition : CreateSuccessSNSTopic
88+
ErrorSNSTopic:
89+
Type: "AWS::SNS::Topic"
90+
Condition : CreateErrorSNSTopic
6491
BackupFunction:
6592
Type: AWS::Serverless::Function
6693
Properties:
@@ -78,6 +105,12 @@ Resources:
78105
Fn::Join:
79106
- ''
80107
- - '{"period_label": "day", "period_format": "%a%H-%M", "keep_count": 14,'
108+
- '"arn": "'
109+
- !If [EnableSuccessSNSTopic, !If [ CreateSuccessSNSTopic, Ref: SuccessSNSTopic, Ref: SuccessSNSTopicOption], '']
110+
- '", '
111+
- '"error_arn": "'
112+
- !If [EnableErrorSNSTopic, !If [ CreateErrorSNSTopic, Ref: ErrorSNSTopic, Ref: ErrorSNSTopicOption], '']
113+
- '", '
81114
- '"ec2_region_name": "'
82115
- Ref: AWS::Region
83116
- '", '
@@ -93,6 +126,12 @@ Resources:
93126
Fn::Join:
94127
- ''
95128
- - '{"period_label": "week", "period_format": "week-%U", "keep_count": 12,'
129+
- '"arn": "'
130+
- !If [EnableSuccessSNSTopic, !If [ CreateSuccessSNSTopic, Ref: SuccessSNSTopic, Ref: SuccessSNSTopicOption], '']
131+
- '", '
132+
- '"error_arn": "'
133+
- !If [EnableErrorSNSTopic, !If [ CreateErrorSNSTopic, Ref: ErrorSNSTopic, Ref: ErrorSNSTopicOption], '']
134+
- '", '
96135
- '"ec2_region_name": "'
97136
- Ref: AWS::Region
98137
- '", '

development.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
appdirs==1.4.0
2+
boto==2.45.0
3+
boto3==1.4.4
4+
botocore==1.5.7
5+
coverage==4.3.4
6+
docutils==0.13.1
7+
futures==3.0.5
8+
httpretty==0.8.10
9+
Jinja2==2.9.5
10+
jmespath==0.9.1
11+
MarkupSafe==0.23
12+
moto==0.4.31
13+
nose==1.3.7
14+
packaging==16.8
15+
pyparsing==2.1.10
16+
python-dateutil==2.6.0
17+
pytz==2016.10
18+
requests==2.13.0
19+
s3transfer==0.1.10
20+
six==1.10.0
21+
Werkzeug==0.11.15
22+
xmltodict==0.10.2

lambda/backuplambda.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import boto3
44
from datetime import datetime
5-
import sys
5+
import sys, traceback
66
import logging
77
import json
88
import pytz
@@ -43,6 +43,9 @@ def list_snapshots_for_resource(self, resource):
4343
def resolve_backupable_id(self, resource):
4444
pass
4545

46+
def resolve_snapshot_name(self, resource):
47+
pass
48+
4649
def resolve_snapshot_time(self, resource):
4750
return resource['StartTime']
4851

@@ -86,9 +89,12 @@ def process_backup(self):
8689
self.snapshot_resource(resource=backup_item, description=description, tags=tags_volume)
8790
self.message += ' New Snapshot created with description: %s and tags: %s\n' % (description, str(tags_volume))
8891
total_creates += 1
89-
except Exception, e:
92+
except Exception as e:
9093
print ("Unexpected error:", sys.exc_info()[0])
9194
print (e)
95+
exc_type, exc_value, exc_traceback = sys.exc_info()
96+
traceback.print_exception(exc_type, exc_value, exc_traceback,
97+
limit=2, file=sys.stdout)
9298
pass
9399

94100
snapshots = self.list_snapshots_for_resource(resource=backup_item)
@@ -104,11 +110,11 @@ def process_backup(self):
104110
else:
105111
print(' Skipping other backup schedule: ' + sndesc)
106112

107-
self.message += "\n Current backups in rotation (keeping {})\n".format(self.keep_count)
113+
self.message += "\n Current backups in rotation (keeping {0})\n".format(self.keep_count)
108114
self.message += " ---------------------------\n"
109115

110116
for snap in deletelist:
111-
self.message += " {} - {}\n".format(self.resolve_snapshot_name(snap),
117+
self.message += " {0} - {1}\n".format(self.resolve_snapshot_name(snap),
112118
self.resolve_snapshot_time(snap))
113119
self.message += " ---------------------------\n"
114120

@@ -123,6 +129,9 @@ def process_backup(self):
123129
except Exception as ex:
124130
print("Unexpected error:", sys.exc_info()[0])
125131
print(ex)
132+
exc_type, exc_value, exc_traceback = sys.exc_info()
133+
traceback.print_exception(exc_type, exc_value, exc_traceback,
134+
limit=2, file=sys.stdout)
126135
logging.error('Error in processing volume with id: ' + backup_id)
127136
self.errmsg += 'Error in processing volume with id: ' + backup_id
128137
count_errors += 1
@@ -140,6 +149,13 @@ def process_backup(self):
140149
self.message += "\nTotal snapshots errors: " + str(count_errors)
141150
self.message += "\nTotal snapshots deleted: " + str(total_deletes) + "\n"
142151

152+
return {
153+
"total_resources": count_total,
154+
"total_creates": total_creates,
155+
"total_errors": count_errors,
156+
"total_deletes": total_deletes,
157+
}
158+
143159
def delete_snapshot(self, snapshot):
144160
pass
145161

@@ -360,7 +376,7 @@ def build_arn_for_id(self, instance_id):
360376
region = self.conn.meta.region_name
361377
account_number = self.resolve_account_number()
362378

363-
return "arn:aws:rds:{}:{}:db:{}".format(region, account_number, instance_id)
379+
return "arn:aws:rds:{0}:{1}:db:{2}".format(region, account_number, instance_id)
364380

365381

366382
def lambda_handler(event, context={}):
@@ -402,13 +418,6 @@ def lambda_handler(event, context={}):
402418

403419
date_suffix = datetime.today().strftime(period_format)
404420

405-
sns_boto = None
406-
407-
# Connect to SNS
408-
if sns_arn or error_sns_arn:
409-
print('Connecting to SNS')
410-
sns_boto = boto3.client('sns', region_name=ec2_region_name)
411-
412421
result = event
413422
if ec2_region_name:
414423
backup_mgr = EC2BackupManager(ec2_region_name=ec2_region_name,
@@ -418,16 +427,24 @@ def lambda_handler(event, context={}):
418427
date_suffix=date_suffix,
419428
keep_count=keep_count)
420429

421-
backup_mgr.process_backup()
430+
metrics = backup_mgr.process_backup()
422431

432+
result["metrics"] = metrics
423433
result["ec2_backup_result"] = backup_mgr.message
424434
print('\n' + backup_mgr.message + '\n')
425435

436+
sns_boto = None
437+
438+
# Connect to SNS
439+
if sns_arn or error_sns_arn:
440+
print('Connecting to SNS')
441+
sns_boto = boto3.client('sns', region_name=ec2_region_name)
442+
426443
if error_sns_arn and backup_mgr.errmsg:
427444
sns_boto.publish(error_sns_arn, 'Error in processing volumes: ' + backup_mgr.errmsg, 'Error with AWS Snapshot')
428445

429446
if sns_arn:
430-
sns_boto.publish(sns_arn, backup_mgr.message, 'Finished AWS EC2 snapshotting')
447+
sns_boto.publish(TopicArn=sns_arn, Message=backup_mgr.message, Subject='Finished AWS EC2 snapshotting')
431448

432449
if rds_region_name:
433450
backup_mgr = RDSBackupManager(rds_region_name=rds_region_name,
@@ -437,15 +454,23 @@ def lambda_handler(event, context={}):
437454
date_suffix=date_suffix,
438455
keep_count=keep_count)
439456

440-
backup_mgr.process_backup()
457+
metrics = backup_mgr.process_backup()
441458

459+
result["metrics"] = metrics
442460
result["rds_backup_result"] = backup_mgr.message
443461
print('\n' + backup_mgr.message + '\n')
444462

463+
sns_boto = None
464+
465+
# Connect to SNS
466+
if sns_arn or error_sns_arn:
467+
print('Connecting to SNS')
468+
sns_boto = boto3.client('sns', region_name=rds_region_name)
469+
445470
if error_sns_arn and backup_mgr.errmsg:
446471
sns_boto.publish(error_sns_arn, 'Error in processing RDS: ' + backup_mgr.errmsg, 'Error with AWS Snapshot')
447472

448473
if sns_arn:
449-
sns_boto.publish(sns_arn, backup_mgr.message, 'Finished AWS RDS snapshotting')
474+
sns_boto.publish(TopicArn=sns_arn, Message=backup_mgr.message, Subject='Finished AWS RDS snapshotting')
450475

451476
return json.dumps(result, indent=2)

setup.cfg

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)