/* * Copyright 2015 herd contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.finra.herd.service; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.IOException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.finra.herd.dao.impl.MockS3OperationsImpl; import org.finra.herd.model.api.xml.Attribute; import org.finra.herd.model.api.xml.BusinessObjectDataKey; import org.finra.herd.model.dto.ConfigurationValue; import org.finra.herd.model.jpa.BusinessObjectDataEntity; import org.finra.herd.model.jpa.BusinessObjectDataStatusEntity; import org.finra.herd.model.jpa.StorageEntity; import org.finra.herd.model.jpa.StoragePlatformEntity; import org.finra.herd.model.jpa.StorageUnitEntity; import org.finra.herd.model.jpa.StorageUnitStatusEntity; import org.finra.herd.service.impl.FileUploadCleanupServiceImpl; /** * This class tests functionality within the FileUploadCleanupService. */ public class FileUploadCleanupServiceTest extends AbstractServiceTest { private String s3BucketName; @Before public void before() { s3BucketName = storageDaoTestHelper.getS3ManagedBucketName(); } /** * Cleans up the S3 test path that we are using. */ @After public void cleanEnv() throws IOException { // Clean up the destination S3 folder. s3Dao.deleteDirectory(s3DaoTestHelper.getTestS3FileTransferRequestParamsDto()); s3Operations.rollback(); } @Test public void testDeleteBusinessObjectData() throws Exception { // Prepare database entries required for testing without creating an S3 file. BusinessObjectDataKey testBusinessObjectKey = new BusinessObjectDataKey(NAMESPACE, BDEF_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE, SUBPARTITION_VALUES, DATA_VERSION); createTestDatabaseEntities(testBusinessObjectKey, STORAGE_NAME, s3BucketName, TARGET_S3_KEY, 15); // Delete the business object data. List<BusinessObjectDataKey> resultBusinessObjectDataKeys = fileUploadCleanupService.deleteBusinessObjectData(STORAGE_NAME, 10); // Validate the results. assertNotNull(resultBusinessObjectDataKeys); assertEquals(1, resultBusinessObjectDataKeys.size()); assertEquals(testBusinessObjectKey, resultBusinessObjectDataKeys.get(0)); validateBusinessObjectDataStatus(testBusinessObjectKey, BusinessObjectDataStatusEntity.DELETED); } @Test public void testDeleteBusinessObjectDataS3FileExists() throws Exception { // Prepare database entries required for testing. BusinessObjectDataKey testBusinessObjectKey = new BusinessObjectDataKey(NAMESPACE, BDEF_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE, SUBPARTITION_VALUES, DATA_VERSION); createTestDatabaseEntities(testBusinessObjectKey, STORAGE_NAME, s3BucketName, TARGET_S3_KEY, 15); // Put a file in S3. PutObjectRequest putObjectRequest = new PutObjectRequest(s3BucketName, TARGET_S3_KEY, new ByteArrayInputStream(new byte[1]), new ObjectMetadata()); s3Operations.putObject(putObjectRequest, null); // Delete the business object data. List<BusinessObjectDataKey> resultBusinessObjectDataKeys = fileUploadCleanupService.deleteBusinessObjectData(STORAGE_NAME, 10); // Validate the results. assertNotNull(resultBusinessObjectDataKeys); assertTrue(resultBusinessObjectDataKeys.isEmpty()); validateBusinessObjectDataStatus(testBusinessObjectKey, BDATA_STATUS); } @Test public void testDeleteBusinessObjectDataAmazonServiceException() throws Exception { // Prepare database entries required for testing. BusinessObjectDataKey testBusinessObjectKey = new BusinessObjectDataKey(NAMESPACE, BDEF_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE, SUBPARTITION_VALUES, DATA_VERSION); createTestDatabaseEntities(testBusinessObjectKey, STORAGE_NAME, s3BucketName, MockS3OperationsImpl.MOCK_S3_FILE_NAME_SERVICE_EXCEPTION, 15); executeWithoutLogging(FileUploadCleanupServiceImpl.class, () -> { // Delete the business object data. List<BusinessObjectDataKey> resultBusinessObjectDataKeys = fileUploadCleanupService.deleteBusinessObjectData(STORAGE_NAME, 10); // Validate the results. assertNotNull(resultBusinessObjectDataKeys); assertTrue(resultBusinessObjectDataKeys.isEmpty()); validateBusinessObjectDataStatus(testBusinessObjectKey, BDATA_STATUS); }); } @Test public void testAbortMultipartUploads() throws Exception { // Prepare database entities required for testing. createTestStorageEntity(STORAGE_NAME, s3BucketName); // Abort multipart uploads started more that 10 minutes ago. int resultAbortedMultipartUploadsCount = fileUploadCleanupService.abortMultipartUploads(STORAGE_NAME, 10); // Validate the result. The mocked multipart listing should list 2 multipart uploads initiated more than 10 minutes ago. assertEquals(2, resultAbortedMultipartUploadsCount); } @Test public void testAbortMultipartUploadsTruncatedMultipartListing() throws Exception { // Prepare database entities required for testing. createTestStorageEntity(STORAGE_NAME, MockS3OperationsImpl.MOCK_S3_BUCKET_NAME_TRUNCATED_MULTIPART_LISTING); // Abort multipart uploads started more that 10 minutes ago. int resultAbortedMultipartUploadsCount = fileUploadCleanupService.abortMultipartUploads(STORAGE_NAME, 10); // Validate the result. The mocked truncated multipart listing should list 4 multipart uploads initiated more than 10 minutes ago. assertEquals(4, resultAbortedMultipartUploadsCount); } @Test public void testAbortMultipartUploadsAmazonServiceException() throws Exception { // Prepare database entities required for testing. createTestStorageEntity(STORAGE_NAME, MockS3OperationsImpl.MOCK_S3_BUCKET_NAME_SERVICE_EXCEPTION); // Try to abort multipart uploads. try { fileUploadCleanupService.abortMultipartUploads(STORAGE_NAME, 10); fail("Should throw an AmazonServiceException."); } catch (AmazonServiceException e) { assertEquals("null (Service: null; Status Code: 0; Error Code: null; Request ID: null)", e.getMessage()); } } private void createTestDatabaseEntities(BusinessObjectDataKey businessObjectDataKey, String storageName, String bucketName, String storageFilePath, int createdOnTimestampMinutesOffset) throws Exception { // Create a storage entity. StorageEntity storageEntity = createTestStorageEntity(storageName, bucketName); // Create a business object data entity. BusinessObjectDataEntity businessObjectDataEntity = businessObjectDataDaoTestHelper.createBusinessObjectDataEntity(businessObjectDataKey, true, BDATA_STATUS); // Apply the offset in minutes to createdOn value. businessObjectDataEntity.setCreatedOn(new Timestamp(businessObjectDataEntity.getCreatedOn().getTime() - createdOnTimestampMinutesOffset * 60 * 1000)); StorageUnitEntity storageUnitEntity = storageUnitDaoTestHelper .createStorageUnitEntity(storageEntity, businessObjectDataEntity, StorageUnitStatusEntity.ENABLED, NO_STORAGE_DIRECTORY_PATH); storageFileDaoTestHelper.createStorageFileEntity(storageUnitEntity, storageFilePath, FILE_SIZE_1_KB, ROW_COUNT_1000); herdDao.saveAndRefresh(businessObjectDataEntity); } private StorageEntity createTestStorageEntity(String storageName, String bucketName) throws Exception { List<Attribute> attributes = new ArrayList<>(); // If specified, populate bucket name attribute for this storage. if (StringUtils.isNotBlank(bucketName)) { attributes.add(new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME), bucketName)); } return storageDaoTestHelper.createStorageEntity(storageName, StoragePlatformEntity.S3, attributes); } private void validateBusinessObjectDataStatus(BusinessObjectDataKey businessObjectDataKey, String expectedBusinessObjectDataStatus) { BusinessObjectDataEntity businessObjectDataEntity = businessObjectDataDao.getBusinessObjectDataByAltKey(businessObjectDataKey); assertNotNull(businessObjectDataEntity); assertEquals(expectedBusinessObjectDataStatus, businessObjectDataEntity.getStatus().getCode()); } }