diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py index b2c1792f2..5af4690ae 100644 --- a/moto/ec2/exceptions.py +++ b/moto/ec2/exceptions.py @@ -231,6 +231,14 @@ class InvalidVolumeAttachmentError(EC2ClientError): ) +class VolumeInUseError(EC2ClientError): + def __init__(self, volume_id, instance_id): + super(VolumeInUseError, self).__init__( + "VolumeInUse", + "Volume {0} is currently attached to {1}".format(volume_id, instance_id), + ) + + class InvalidDomainError(EC2ClientError): def __init__(self, domain): super(InvalidDomainError, self).__init__( diff --git a/moto/ec2/models.py b/moto/ec2/models.py index be39bab28..bf4936d09 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -70,6 +70,7 @@ from .exceptions import ( InvalidSubnetIdError, InvalidSubnetRangeError, InvalidVolumeIdError, + VolumeInUseError, InvalidVolumeAttachmentError, InvalidVpcCidrBlockAssociationIdError, InvalidVPCPeeringConnectionIdError, @@ -2385,6 +2386,9 @@ class EBSBackend(object): def delete_volume(self, volume_id): if volume_id in self.volumes: + volume = self.volumes[volume_id] + if volume.attachment: + raise VolumeInUseError(volume_id, volume.attachment.instance.id) return self.volumes.pop(volume_id) raise InvalidVolumeIdError(volume_id) diff --git a/tests/test_ec2/test_elastic_block_store.py b/tests/test_ec2/test_elastic_block_store.py index 3c7e17ec8..4bd2a8dfa 100644 --- a/tests/test_ec2/test_elastic_block_store.py +++ b/tests/test_ec2/test_elastic_block_store.py @@ -53,6 +53,45 @@ def test_create_and_delete_volume(): cm.exception.request_id.should_not.be.none +@mock_ec2_deprecated +def test_delete_attached_volume(): + conn = boto.ec2.connect_to_region("us-east-1") + reservation = conn.run_instances("ami-1234abcd") + # create an instance + instance = reservation.instances[0] + # create a volume + volume = conn.create_volume(80, "us-east-1a") + # attach volume to instance + volume.attach(instance.id, "/dev/sdh") + + volume.update() + volume.volume_state().should.equal("in-use") + volume.attachment_state().should.equal("attached") + + volume.attach_data.instance_id.should.equal(instance.id) + + # attempt to delete volume + # assert raises VolumeInUseError + with assert_raises(EC2ResponseError) as ex: + volume.delete() + ex.exception.error_code.should.equal("VolumeInUse") + ex.exception.status.should.equal(400) + ex.exception.message.should.equal( + "Volume {0} is currently attached to {1}".format(volume.id, instance.id) + ) + + volume.detach() + + volume.update() + volume.volume_state().should.equal("available") + + volume.delete() + + all_volumes = conn.get_all_volumes() + my_volume = [item for item in all_volumes if item.id == volume.id] + my_volume.should.have.length_of(0) + + @mock_ec2_deprecated def test_create_encrypted_volume_dryrun(): conn = boto.ec2.connect_to_region("us-east-1")