def test_delete_cloudtrail_unexpected_error_code(self): """ Test delete_cloudtrail handles when AWS raises an unexpected error code. This could happen if AWS is misbehaving unexpectedly. We treat this as a non-blocking failure and simply log messages. """ client_error = ClientError( error_response={"Error": { "Code": "Potatoes" }}, operation_name=Mock(), ) expected_errors = [ "Unexpected error Potatoes occurred disabling CloudTrail", f"AwsCloudAccount ID {self.account.id}", ] with self.assertLogs( "api.clouds.aws.util", level="ERROR") as logger, patch.object( util.aws, "get_session"), patch.object( util.aws, "delete_cloudtrail") as mock_delete_cloudtrail: mock_delete_cloudtrail.side_effect = client_error success = util.delete_cloudtrail(self.account.content_object) mock_delete_cloudtrail.assert_called() self.assertIn("Traceback", logger.output[0]) # from logger.exception for expected_error in expected_errors: self.assertIn(expected_error, logger.output[1]) # from logger.error self.assertFalse(success)
def test_delete_cloudtrail_access_denied(self): """ Test delete_cloudtrail handles when AWS raises an AccessDenied error. This could happen if the user has deleted the AWS account or role. We treat this as a non-blocking failure and simply log messages. """ client_error = ClientError( error_response={"Error": { "Code": "AccessDenied" }}, operation_name=Mock(), ) expected_warnings = [ "encountered AccessDenied and cannot delete cloudtrail", f"CloudAccount ID {self.account.id}", ] with self.assertLogs( "api.clouds.aws.util", level="WARNING") as logger, patch.object( util.aws, "get_session"), patch.object( util.aws, "delete_cloudtrail") as mock_delete_cloudtrail: mock_delete_cloudtrail.side_effect = client_error success = util.delete_cloudtrail(self.account.content_object) mock_delete_cloudtrail.assert_called() for expected_warning in expected_warnings: self.assertIn(expected_warning, logger.output[0]) self.assertFalse(success)
def test_delete_cloudtrail_success(self): """Test delete_cloudtrail normal happy path.""" with patch.object(util.aws, "get_session"), patch.object( util.aws, "delete_cloudtrail") as mock_delete_cloudtrail: success = util.delete_cloudtrail(self.account.content_object) mock_delete_cloudtrail.assert_called() self.assertTrue(success)
def _delete_cloudtrail(aws_cloud_account): """Delete the given AwsCloudAccount's AWS CloudTrail.""" logger.info( _("Attempting _delete_cloudtrail for %(account)s"), {"account": aws_cloud_account}, ) try: cloud_account = aws_cloud_account.cloud_account.get() if cloud_account.is_enabled: logger.warning( _("Aborting _delete_cloudtrail because CloudAccount ID " "%(cloud_account_id)s is enabled."), {"cloud_account_id": cloud_account.id}, ) return except CloudAccount.DoesNotExist: pass from api.clouds.aws import util # Avoid circular import. if not util.delete_cloudtrail(aws_cloud_account): logger.info( _("Failed to delete CloudTrail when disabling AwsCloudAccount ID " "%(aws_cloud_account_id)s (AWS account ID %(aws_account_id)s)"), { "aws_cloud_account_id": aws_cloud_account.id, "aws_account_id": aws_cloud_account.aws_account_id, }, )
def test_delete_cloudtrail_not_found(self): """ Test delete_cloudtrail handles when AWS raises an TrailNotFoundException error. This could happen if the trail has already been deleted. We treat this as a success since the same effective outcome is no trail for us. """ client_error = ClientError( error_response={"Error": { "Code": "TrailNotFoundException" }}, operation_name=Mock(), ) with patch.object(util.aws, "get_session"), patch.object( util.aws, "delete_cloudtrail") as mock_delete_cloudtrail: mock_delete_cloudtrail.side_effect = client_error success = util.delete_cloudtrail(self.account.content_object) mock_delete_cloudtrail.assert_called() self.assertTrue(success)