def test_perform_snapshot_ignore_instance(mocker): """Test for method of the same name.""" # some default settings for this test region = 'us-west-2' snapshot_settings = { 'snapshot': { 'minimum': 5, 'frequency': '2 hours', 'retention': '5 days' }, 'match': { 'tag:backup': 'yes' }, 'ignore': [] } # create an instance and record the id instance_id = mocks.create_instances(region, count=1)[0] snapshot_settings['ignore'].append(instance_id) # need to filter instances, so need dynamodb present mocks.create_dynamodb('us-east-1') dynamo.store_configuration('us-east-1', 'some_unique_id', '111122223333', snapshot_settings) # patch the final method that takes a snapshot mocker.patch('ebs_snapper.utils.snapshot_and_tag') # since there are no snapshots, we should expect this to trigger one ctx = utils.MockContext() snapshot.perform_snapshot(ctx, region) # test results utils.snapshot_and_tag.assert_not_called() # pylint: disable=E1103
def test_clean_snapshots_tagged_timeout(mocker): """Test that we _DONT_ clean anything if runtime > 4 minutes""" # default settings region = 'us-east-1' mocks.create_dynamodb(region) ctx = utils.MockContext() ctx.set_remaining_time_in_millis(5) # 5 millis remaining # create an instance and record the id instance_id = mocks.create_instances(region, count=1)[0] # setup the min # snaps for the instance config_data = { "match": {"instance-id": instance_id}, "snapshot": { "retention": "6 days", "minimum": 0, "frequency": "13 hours" } } # put it in the table, be sure it succeeded dynamo.store_configuration(region, 'foo', '111122223333', config_data) # figure out the EBS volume that came with our instance volume_id = utils.get_volumes([instance_id], region)[0]['VolumeId'] # make a snapshot that should be deleted today too now = datetime.datetime.now(dateutil.tz.tzutc()) delete_on = now.strftime('%Y-%m-%d') utils.snapshot_and_tag(instance_id, 'ami-123abc', volume_id, delete_on, region) mocker.patch('ebs_snapper.utils.delete_snapshot') clean.clean_snapshot(ctx, region) # ensure we DO NOT take a snapshot if our runtime was 5 minutes assert not utils.delete_snapshot.called
def test_get_instance(): """Test for method of the same name""" # def get_instance(instance_id, region): region = 'us-west-2' instance_id = mocks.create_instances(region, count=1)[0] found_instance = utils.get_instance(instance_id, region) assert found_instance['InstanceId'] == instance_id
def test_perform_fanout_all_regions_clean(mocker): """Test for method of the same name.""" mocks.create_sns_topic('CleanSnapshotTopic') mocks.create_dynamodb() expected_regions = utils.get_regions() for r in expected_regions: # must have an instance in the region to clean it mocks.create_instances(region=r) expected_sns_topic = utils.get_topic_arn('CleanSnapshotTopic', 'us-east-1') ctx = utils.MockContext() mocker.patch('ebs_snapper.clean.send_fanout_message') # fan out, and be sure we touched every region clean.perform_fanout_all_regions(ctx) for r in expected_regions: clean.send_fanout_message.assert_any_call( # pylint: disable=E1103 ctx, cli=False, region=r, topic_arn=expected_sns_topic)
def test_clean_tagged_snapshots_ignore_volume(mocker): """Test for method of the same name.""" # default settings region = 'us-east-1' mocks.create_dynamodb(region) # create an instance and record the id instance_id = mocks.create_instances(region, count=1)[0] ctx = utils.MockContext() # setup the min # snaps for the instance config_data = { "match": { "instance-id": instance_id }, "snapshot": { "retention": "6 days", "minimum": 0, "frequency": "13 hours" }, "ignore": [] } # put it in the table, be sure it succeeded dynamo.store_configuration(region, 'foo', AWS_MOCK_ACCOUNT, config_data) # figure out the EBS volume that came with our instance volume_id = utils.get_volumes([instance_id], region)[0]['VolumeId'] config_data["ignore"].append(volume_id) # make a snapshot that should be deleted today too now = datetime.datetime.now(dateutil.tz.tzutc()) delete_on = now.strftime('%Y-%m-%d') utils.snapshot_and_tag(instance_id, 'ami-123abc', volume_id, delete_on, region) snapshot_id = utils.most_recent_snapshot(volume_id, region)['SnapshotId'] mocker.patch('ebs_snapper.utils.delete_snapshot') clean.clean_snapshot(ctx, region) # ensure we deleted this snapshot if it was ready to die today utils.delete_snapshot.assert_any_call(snapshot_id, region) # pylint: disable=E1103 # now raise the minimum, and check to be sure we didn't delete utils.delete_snapshot.reset_mock() # pylint: disable=E1103 config_data['snapshot']['minimum'] = 5 dynamo.store_configuration(region, 'foo', AWS_MOCK_ACCOUNT, config_data) clean.clean_snapshot(ctx, region) utils.delete_snapshot.assert_not_called() # pylint: disable=E1103
def test_clean_tagged_snapshots_ignore_retention(mocker): """Test for method of the same name.""" # default settings region = 'us-east-1' mocks.create_dynamodb(region) # create two instances and record the id of one of them instance_id_list = mocks.create_instances(region, count=2) instance_id = instance_id_list[0] ctx = utils.MockContext() # setup the min # snaps for the instance config_data = { "match": { "instance-id": instance_id }, "snapshot": { "retention": "6 days", "minimum": 5, "frequency": "13 hours" }, "ignore_retention": True } # put it in the table, be sure it succeeded dynamo.store_configuration(region, 'foo', AWS_MOCK_ACCOUNT, config_data) # figure out the EBS volume that came with our instance volume_id = utils.get_volumes([instance_id], region)[0]['VolumeId'] # make a snapshot that should be deleted today (but 5 snap retention) now = datetime.datetime.now(dateutil.tz.tzutc()) delete_on = now.strftime('%Y-%m-%d') utils.snapshot_and_tag(instance_id, 'ami-123abc', volume_id, delete_on, region) snapshot_id = utils.most_recent_snapshot(volume_id, region)['SnapshotId'] # now delete the instance and volume, keep the snapshot client = boto3.client('ec2', region_name=region) client.terminate_instances(InstanceIds=[instance_id]) mocker.patch('ebs_snapper.utils.delete_snapshot') dynamo.store_configuration(region, 'foo', AWS_MOCK_ACCOUNT, config_data) clean.clean_snapshot(ctx, region) utils.delete_snapshot.assert_any_call(snapshot_id, region) # pylint: disable=E1103
def test_perform_snapshot_skipped(mocker): """Test for method of the same name.""" # some default settings for this test region = 'us-west-2' snapshot_settings = { 'snapshot': { 'minimum': 5, 'frequency': '2 hours', 'retention': '5 days' }, 'match': { 'tag:backup': 'yes' } } mocks.create_dynamodb('us-east-1') dynamo.store_configuration('us-east-1', 'some_unique_id', '111122223333', snapshot_settings) # create an instance and record the id instance_id = mocks.create_instances(region, count=1)[0] # figure out the EBS volume that came with our instance instance_details = utils.get_instance(instance_id, region) block_devices = instance_details.get('BlockDeviceMappings', []) volume_id = block_devices[0]['Ebs']['VolumeId'] # determine what we should be tagging the snapshot ret, freq = utils.parse_snapshot_settings(snapshot_settings) # pylint: disable=unused-variable now = datetime.datetime.now(dateutil.tz.tzutc()) delete_on_dt = now + ret delete_on = delete_on_dt.strftime('%Y-%m-%d') # now take a snapshot, so we expect this next one to be skipped utils.snapshot_and_tag(instance_id, 'ami-123abc', volume_id, delete_on, region) # patch the final method that takes a snapshot mocker.patch('ebs_snapper.utils.snapshot_and_tag') # since there are no snapshots, we should expect this to trigger one ctx = utils.MockContext() snapshot.perform_snapshot(ctx, region) # test results (should not create a second snapshot) utils.snapshot_and_tag.assert_not_called() # pylint: disable=E1103
def test_perform_snapshot_ignore_volume(mocker): """Test for method of the same name.""" # some default settings for this test region = 'us-west-2' snapshot_settings = { 'snapshot': { 'minimum': 5, 'frequency': '2 hours', 'retention': '5 days' }, 'match': { 'tag:backup': 'yes' }, 'ignore': [] } # create an instance and record the id instance_id = mocks.create_instances(region, count=1)[0] # need to filter instances, so need dynamodb present mocks.create_dynamodb('us-east-1') dynamo.store_configuration('us-east-1', 'some_unique_id', AWS_MOCK_ACCOUNT, snapshot_settings) # figure out the EBS volume that came with our instance instance_details = utils.get_instance(instance_id, region) block_devices = instance_details.get('BlockDeviceMappings', []) volume_id = block_devices[0]['Ebs']['VolumeId'] snapshot_settings['ignore'].append(volume_id) # patch the final method that takes a snapshot mocker.patch('ebs_snapper.utils.snapshot_and_tag') # since there are no snapshots, we should expect this to trigger one ctx = utils.MockContext() snapshot.perform_snapshot(ctx, region) # test results utils.snapshot_and_tag.assert_not_called() # pylint: disable=E1103
def test_perform_snapshot(mocker): """Test for method of the same name.""" # some default settings for this test region = 'us-west-2' snapshot_settings = { 'snapshot': {'minimum': 5, 'frequency': '2 hours', 'retention': '5 days'}, 'match': {'tag:backup': 'yes'} } # create an instance and record the id instance_id = mocks.create_instances(region, count=1)[0] # need to filter instances, so need dynamodb present mocks.create_dynamodb('us-east-1') dynamo.store_configuration('us-east-1', 'some_unique_id', '111122223333', snapshot_settings) # figure out the EBS volume that came with our instance instance_details = utils.get_instance(instance_id, region) block_devices = instance_details.get('BlockDeviceMappings', []) volume_id = block_devices[0]['Ebs']['VolumeId'] # determine what we should be tagging the snapshot ret, freq = utils.parse_snapshot_settings(snapshot_settings) # pylint: disable=unused-variable now = datetime.datetime.now(dateutil.tz.tzutc()) delete_on_dt = now + ret delete_on = delete_on_dt.strftime('%Y-%m-%d') # apply some tags client = boto3.client('ec2', region_name=region) instance_tags = [ {'Key': 'Name', 'Value': 'Foo'}, {'Key': 'Service', 'Value': 'Bar'}, {'Key': 'backup', 'Value': 'yes'}, ] client.create_tags(DryRun=False, Resources=[instance_id], Tags=instance_tags) # override one of the tags volume_tags = [{'Key': 'Service', 'Value': 'Baz'}] client.create_tags(DryRun=False, Resources=[volume_id], Tags=volume_tags) # when combined, we expect tags to be this. tags = [ {'Key': 'Name', 'Value': 'Foo'}, {'Key': 'Service', 'Value': 'Baz'}, {'Key': 'backup', 'Value': 'yes'}, ] # patch the final method that takes a snapshot mocker.patch('ebs_snapper.utils.snapshot_and_tag') # since there are no snapshots, we should expect this to trigger one ctx = utils.MockContext() snapshot.perform_snapshot(ctx, region) # test results utils.snapshot_and_tag.assert_any_call( # pylint: disable=E1103 instance_id, 'ami-123abc', volume_id, delete_on, region, additional_tags=tags)
def test_calculate_relevant_tags(): """Confirm that tags are calculated correctly, and don't exceed 10""" # client.create_tags() region = 'us-west-2' client = boto3.client('ec2', region_name=region) # some instance tags (pad to fill it after) instance_tags = [ { 'Key': 'Foo', 'Value': 'Bar' }, # normal tag { 'Key': 'BusinessUnit', 'Value': 'Dept1' } # billing tag ] for i in xrange(0, 8): instance_tags.append({ 'Key': "foo-" + str(i), 'Value': "bar-" + str(i) }) # some volume tags (pad to fill it after) volume_tags = [ { 'Key': 'Foo', 'Value': 'Baz' }, # more normal tags { 'Key': 'BusinessUnit', 'Value': 'Dept2' }, # billing tag override { 'Key': 'Cluster', 'Value': 'Bank' } # billing tag that won't override ] # 47 because volumes can only have 50 tags too for i in xrange(0, 47): volume_tags.append({ 'Key': "foo-" + str(i), 'Value': "bar-" + str(i + 100) }) # create an instance and record the id instance_id = mocks.create_instances(region, count=1)[0] client.create_tags(Resources=[instance_id], Tags=instance_tags) # figure out the EBS volume that came with our instance volume_id = utils.get_volumes([instance_id], region)[0]['VolumeId'] client.create_tags(Resources=[volume_id], Tags=volume_tags) # make some snapshots that should be deleted today now = datetime.now(dateutil.tz.tzutc()) delete_on = now.strftime('%Y-%m-%d') instance_data = utils.get_instance(instance_id, region=region) volume_data = utils.get_volume(volume_id, region=region) expected_tags = utils.calculate_relevant_tags( instance_data.get('Tags', None), volume_data.get('Tags', None)) # create the snapshot utils.snapshot_and_tag(instance_id, 'ami-123abc', volume_id, delete_on, region, additional_tags=expected_tags) # now confirm the tags are correct snapshots = utils.get_snapshots_by_volume(volume_id, region) created_snap = snapshots[0] # check those expected tags expected_pairs = { 'BusinessUnit': 'Dept2', 'Cluster': 'Bank', 'DeleteOn': delete_on, # moto returns tags in very random order, for testing purposes, # so I can't really test anything else with the foo-* tags here } for k, v in expected_pairs.iteritems(): assert {'Key': k, 'Value': v} in created_snap['Tags'] # be sure we don't include the last tag of the 50, it should have been chopped off # because of the DeleteOn tag taking an extra space, and 50 tags + DeleteOn > 50 max tags assert { 'Key': 'foo-47', 'Value': 'bar-' + str(47 + 100) } not in created_snap['Tags']