modify to use create_snapshot, add extra tests for certain error conditions
This commit is contained in:
parent
f2cc60b999
commit
c84e8c86f0
@ -74,4 +74,20 @@ class InvalidDBInstanceStateError(RDSClientError):
|
|||||||
estate = "in available state" if istate == 'stop' else "stopped, it cannot be started"
|
estate = "in available state" if istate == 'stop' else "stopped, it cannot be started"
|
||||||
super(InvalidDBInstanceStateError, self).__init__(
|
super(InvalidDBInstanceStateError, self).__init__(
|
||||||
'InvalidDBInstanceState',
|
'InvalidDBInstanceState',
|
||||||
'when calling the {}DBInstance operation: Instance {} is not {}.'.format(istate.title(), database_identifier, estate))
|
'Instance {} is not {}.'.format(database_identifier, estate))
|
||||||
|
|
||||||
|
|
||||||
|
class SnapshotQuotaExceededError(RDSClientError):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(SnapshotQuotaExceededError, self).__init__(
|
||||||
|
'SnapshotQuotaExceeded',
|
||||||
|
'The request cannot be processed because it would exceed the maximum number of snapshots.')
|
||||||
|
|
||||||
|
|
||||||
|
class DBSnapshotAlreadyExistsError(RDSClientError):
|
||||||
|
|
||||||
|
def __init__(self, database_snapshot_identifier):
|
||||||
|
super(DBSnapshotAlreadyExistsError, self).__init__(
|
||||||
|
'DBSnapshotAlreadyExists',
|
||||||
|
'Cannot create the snapshot because a snapshot with the identifier {} already exists.'.format(database_snapshot_identifier))
|
||||||
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
import os
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import boto.rds2
|
import boto.rds2
|
||||||
@ -20,7 +21,9 @@ from .exceptions import (RDSClientError,
|
|||||||
DBSubnetGroupNotFoundError,
|
DBSubnetGroupNotFoundError,
|
||||||
DBParameterGroupNotFoundError,
|
DBParameterGroupNotFoundError,
|
||||||
InvalidDBClusterStateFaultError,
|
InvalidDBClusterStateFaultError,
|
||||||
InvalidDBInstanceStateError)
|
InvalidDBInstanceStateError,
|
||||||
|
SnapshotQuotaExceededError,
|
||||||
|
DBSnapshotAlreadyExistsError)
|
||||||
|
|
||||||
|
|
||||||
class Database(BaseModel):
|
class Database(BaseModel):
|
||||||
@ -410,6 +413,7 @@ class Snapshot(BaseModel):
|
|||||||
self.tags = tags or []
|
self.tags = tags or []
|
||||||
self.created_at = iso_8601_datetime_with_milliseconds(datetime.datetime.now())
|
self.created_at = iso_8601_datetime_with_milliseconds(datetime.datetime.now())
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def snapshot_arn(self):
|
def snapshot_arn(self):
|
||||||
return "arn:aws:rds:{0}:1234567890:snapshot:{1}".format(self.database.region, self.snapshot_id)
|
return "arn:aws:rds:{0}:1234567890:snapshot:{1}".format(self.database.region, self.snapshot_id)
|
||||||
@ -676,10 +680,14 @@ class RDS2Backend(BaseBackend):
|
|||||||
self.databases[database_id] = database
|
self.databases[database_id] = database
|
||||||
return database
|
return database
|
||||||
|
|
||||||
def create_snapshot(self, db_instance_identifier, db_snapshot_identifier, tags):
|
def create_snapshot(self, db_instance_identifier, db_snapshot_identifier, tags=None):
|
||||||
database = self.databases.get(db_instance_identifier)
|
database = self.databases.get(db_instance_identifier)
|
||||||
if not database:
|
if not database:
|
||||||
raise DBInstanceNotFoundError(db_instance_identifier)
|
raise DBInstanceNotFoundError(db_instance_identifier)
|
||||||
|
if db_snapshot_identifier in self.snapshots:
|
||||||
|
raise DBSnapshotAlreadyExistsError(db_snapshot_identifier)
|
||||||
|
if len(self.snapshots) >= int(os.environ.get('MOTO_RDS_SNAPSHOT_LIMIT', '100')):
|
||||||
|
raise SnapshotQuotaExceededError()
|
||||||
snapshot = Snapshot(database, db_snapshot_identifier, tags)
|
snapshot = Snapshot(database, db_snapshot_identifier, tags)
|
||||||
self.snapshots[db_snapshot_identifier] = snapshot
|
self.snapshots[db_snapshot_identifier] = snapshot
|
||||||
return snapshot
|
return snapshot
|
||||||
@ -743,9 +751,8 @@ class RDS2Backend(BaseBackend):
|
|||||||
raise InvalidDBClusterStateFaultError(db_instance_identifier)
|
raise InvalidDBClusterStateFaultError(db_instance_identifier)
|
||||||
if database.status != 'available':
|
if database.status != 'available':
|
||||||
raise InvalidDBInstanceStateError(db_instance_identifier, 'stop')
|
raise InvalidDBInstanceStateError(db_instance_identifier, 'stop')
|
||||||
# todo: create rds snapshots
|
if db_snapshot_identifier:
|
||||||
# if db_snapshot_identifier:
|
self.create_snapshot(db_instance_identifier, db_snapshot_identifier)
|
||||||
# self.create_rds_snapshot(db_instance_identifier, db_snapshot_identifier)
|
|
||||||
database.status = 'shutdown'
|
database.status = 'shutdown'
|
||||||
return database
|
return database
|
||||||
|
|
||||||
@ -757,13 +764,6 @@ class RDS2Backend(BaseBackend):
|
|||||||
database.status = 'available'
|
database.status = 'available'
|
||||||
return database
|
return database
|
||||||
|
|
||||||
# def create_rds_snapshot(self, db_instance_identifier, db_snapshot_identifier):
|
|
||||||
# database = self.describe_databases(db_instance_identifier)[0]
|
|
||||||
# # todo
|
|
||||||
# # DBSnapshotAlreadyExists
|
|
||||||
# # SnapshotQuotaExceeded
|
|
||||||
# return None
|
|
||||||
|
|
||||||
def find_db_from_id(self, db_id):
|
def find_db_from_id(self, db_id):
|
||||||
if self.arn_regex.match(db_id):
|
if self.arn_regex.match(db_id):
|
||||||
arn_breakdown = db_id.split(':')
|
arn_breakdown = db_id.split(':')
|
||||||
|
@ -31,6 +31,7 @@ def test_create_database():
|
|||||||
database['DBInstance']['DBName'].should.equal('staging-postgres')
|
database['DBInstance']['DBName'].should.equal('staging-postgres')
|
||||||
database['DBInstance']['DBInstanceIdentifier'].should.equal("db-master-1")
|
database['DBInstance']['DBInstanceIdentifier'].should.equal("db-master-1")
|
||||||
|
|
||||||
|
|
||||||
@mock_rds2
|
@mock_rds2
|
||||||
def test_stop_database():
|
def test_stop_database():
|
||||||
conn = boto3.client('rds', region_name='us-west-2')
|
conn = boto3.client('rds', region_name='us-west-2')
|
||||||
@ -46,12 +47,17 @@ def test_stop_database():
|
|||||||
DBSecurityGroups=["my_sg"])
|
DBSecurityGroups=["my_sg"])
|
||||||
mydb = conn.describe_db_instances(DBInstanceIdentifier=database['DBInstance']['DBInstanceIdentifier'])['DBInstances'][0]
|
mydb = conn.describe_db_instances(DBInstanceIdentifier=database['DBInstance']['DBInstanceIdentifier'])['DBInstances'][0]
|
||||||
mydb['DBInstanceStatus'].should.equal('available')
|
mydb['DBInstanceStatus'].should.equal('available')
|
||||||
# test stopping database
|
# test stopping database should shutdown
|
||||||
response = conn.stop_db_instance(DBInstanceIdentifier=mydb['DBInstanceIdentifier'])
|
response = conn.stop_db_instance(DBInstanceIdentifier=mydb['DBInstanceIdentifier'])
|
||||||
response['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
response['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||||
response['DBInstance']['DBInstanceStatus'].should.equal('shutdown')
|
response['DBInstance']['DBInstanceStatus'].should.equal('shutdown')
|
||||||
# test rdsclient error when trying to stop an already stopped database
|
# test rdsclient error when trying to stop an already stopped database
|
||||||
conn.stop_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier']).should.throw(ClientError)
|
conn.stop_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier']).should.throw(ClientError)
|
||||||
|
# test stopping a stopped database with snapshot should error and no snapshot should exist for that call
|
||||||
|
conn.stop_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier'], DBSnapshotIdentifier='rocky4570-rds-snap').should.throw(ClientError)
|
||||||
|
response = conn.describe_db_snapshots()
|
||||||
|
response['DBSnapshots'].should.equal([])
|
||||||
|
|
||||||
|
|
||||||
@mock_rds2
|
@mock_rds2
|
||||||
def test_start_database():
|
def test_start_database():
|
||||||
@ -68,14 +74,27 @@ def test_start_database():
|
|||||||
DBSecurityGroups=["my_sg"])
|
DBSecurityGroups=["my_sg"])
|
||||||
mydb = conn.describe_db_instances(DBInstanceIdentifier=database['DBInstance']['DBInstanceIdentifier'])['DBInstances'][0]
|
mydb = conn.describe_db_instances(DBInstanceIdentifier=database['DBInstance']['DBInstanceIdentifier'])['DBInstances'][0]
|
||||||
mydb['DBInstanceStatus'].should.equal('available')
|
mydb['DBInstanceStatus'].should.equal('available')
|
||||||
# test trying to start an already started database
|
# test starting an already started database should error
|
||||||
conn.start_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier']).should.throw(ClientError)
|
conn.start_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier']).should.throw(ClientError)
|
||||||
# stop and test start - should go from shutdown to available
|
# stop and test start - should go from shutdown to available, create snapshot and check snapshot
|
||||||
response = conn.stop_db_instance(DBInstanceIdentifier=mydb['DBInstanceIdentifier'])
|
response = conn.stop_db_instance(DBInstanceIdentifier=mydb['DBInstanceIdentifier'], DBSnapshotIdentifier='rocky4570-rds-snap')
|
||||||
|
response['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||||
response['DBInstance']['DBInstanceStatus'].should.equal('shutdown')
|
response['DBInstance']['DBInstanceStatus'].should.equal('shutdown')
|
||||||
|
response = conn.describe_db_snapshots()
|
||||||
|
response['DBSnapshots'][0]['DBSnapshotIdentifier'].should.equal('rocky4570-rds-snap')
|
||||||
response = conn.start_db_instance(DBInstanceIdentifier=mydb['DBInstanceIdentifier'])
|
response = conn.start_db_instance(DBInstanceIdentifier=mydb['DBInstanceIdentifier'])
|
||||||
response['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
response['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||||
response['DBInstance']['DBInstanceStatus'].should.equal('available')
|
response['DBInstance']['DBInstanceStatus'].should.equal('available')
|
||||||
|
# starting database should not remove snapshot
|
||||||
|
response = conn.describe_db_snapshots()
|
||||||
|
response['DBSnapshots'][0]['DBSnapshotIdentifier'].should.equal('rocky4570-rds-snap')
|
||||||
|
# test stopping database, create snapshot with existing snapshot already created should throw error
|
||||||
|
conn.stop_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier'], DBSnapshotIdentifier='rocky4570-rds-snap').should.throw(ClientError)
|
||||||
|
# test stopping database not invoking snapshot should succeed.
|
||||||
|
response = conn.stop_db_instance(DBInstanceIdentifier=mydb['DBInstanceIdentifier'])
|
||||||
|
response['ResponseMetadata']['HTTPStatusCode'].should.equal(200)
|
||||||
|
response['DBInstance']['DBInstanceStatus'].should.equal('shutdown')
|
||||||
|
|
||||||
|
|
||||||
@mock_rds2
|
@mock_rds2
|
||||||
def test_fail_to_stop_multi_az():
|
def test_fail_to_stop_multi_az():
|
||||||
@ -99,6 +118,7 @@ def test_fail_to_stop_multi_az():
|
|||||||
# multi-az databases arent allowed to be started up at this time.
|
# multi-az databases arent allowed to be started up at this time.
|
||||||
conn.start_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier']).should.throw(ClientError)
|
conn.start_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier']).should.throw(ClientError)
|
||||||
|
|
||||||
|
|
||||||
@mock_rds2
|
@mock_rds2
|
||||||
def test_fail_to_stop_readreplica():
|
def test_fail_to_stop_readreplica():
|
||||||
conn = boto3.client('rds', region_name='us-west-2')
|
conn = boto3.client('rds', region_name='us-west-2')
|
||||||
@ -124,6 +144,48 @@ def test_fail_to_stop_readreplica():
|
|||||||
# read-replicas are not allowed to be started at this time.
|
# read-replicas are not allowed to be started at this time.
|
||||||
conn.start_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier']).should.throw(ClientError)
|
conn.start_db_instance.when.called_with(DBInstanceIdentifier=mydb['DBInstanceIdentifier']).should.throw(ClientError)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_rds2
|
||||||
|
def test_snapshotquota_exceeded():
|
||||||
|
import os
|
||||||
|
conn = boto3.client('rds', region_name='us-west-2')
|
||||||
|
database1 = conn.create_db_instance(DBInstanceIdentifier='db-master-1',
|
||||||
|
AllocatedStorage=10,
|
||||||
|
Engine='postgres',
|
||||||
|
DBName='staging-postgres',
|
||||||
|
DBInstanceClass='db.m1.small',
|
||||||
|
LicenseModel='license-included',
|
||||||
|
MasterUsername='root',
|
||||||
|
MasterUserPassword='hunter2',
|
||||||
|
Port=1234,
|
||||||
|
DBSecurityGroups=["my_sg"])
|
||||||
|
database2 = conn.create_db_instance(DBInstanceIdentifier='db-master-2',
|
||||||
|
AllocatedStorage=10,
|
||||||
|
Engine='postgres',
|
||||||
|
DBName='staging-postgres',
|
||||||
|
DBInstanceClass='db.m1.small',
|
||||||
|
LicenseModel='license-included',
|
||||||
|
MasterUsername='root',
|
||||||
|
MasterUserPassword='hunter2',
|
||||||
|
Port=1234,
|
||||||
|
DBSecurityGroups=["my_sg"])
|
||||||
|
database3 = conn.create_db_instance(DBInstanceIdentifier='db-master-3',
|
||||||
|
AllocatedStorage=10,
|
||||||
|
Engine='postgres',
|
||||||
|
DBName='staging-postgres',
|
||||||
|
DBInstanceClass='db.m1.small',
|
||||||
|
LicenseModel='license-included',
|
||||||
|
MasterUsername='root',
|
||||||
|
MasterUserPassword='hunter2',
|
||||||
|
Port=1234,
|
||||||
|
DBSecurityGroups=["my_sg"])
|
||||||
|
conn.stop_db_instance(DBInstanceIdentifier=database1['DBInstance']['DBInstanceIdentifier'], DBSnapshotIdentifier='rocky4570-rds-snap1')
|
||||||
|
conn.stop_db_instance(DBInstanceIdentifier=database2['DBInstance']['DBInstanceIdentifier'], DBSnapshotIdentifier='rocky4570-rds-snap2')
|
||||||
|
os.environ['MOTO_RDS_SNAPSHOT_LIMIT'] = '2'
|
||||||
|
conn.stop_db_instance.when.called_with(DBInstanceIdentifier=database3['DBInstance']['DBInstanceIdentifier'], DBSnapshotIdentifier='rocky4570-rds-snap3').should.throw(ClientError)
|
||||||
|
os.unsetenv('MOTO_RDS_SNAPSHOT_LIMIT')
|
||||||
|
|
||||||
|
|
||||||
@mock_rds2
|
@mock_rds2
|
||||||
def test_get_databases():
|
def test_get_databases():
|
||||||
conn = boto3.client('rds', region_name='us-west-2')
|
conn = boto3.client('rds', region_name='us-west-2')
|
||||||
|
Loading…
Reference in New Issue
Block a user