/* * 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.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.util.Arrays; import java.util.List; import com.amazonaws.services.s3.Headers; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.StorageClass; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.finra.herd.dao.impl.MockS3OperationsImpl; import org.finra.herd.model.ObjectNotFoundException; import org.finra.herd.model.api.xml.BusinessObjectDataKey; import org.finra.herd.model.dto.S3FileTransferRequestParamsDto; import org.finra.herd.model.dto.StorageUnitAlternateKeyDto; import org.finra.herd.model.jpa.BusinessObjectDataEntity; import org.finra.herd.model.jpa.StorageFileEntity; import org.finra.herd.model.jpa.StorageUnitEntity; import org.finra.herd.model.jpa.StorageUnitStatusEntity; import org.finra.herd.service.impl.BusinessObjectDataFinalizeRestoreServiceImpl; /** * This class tests functionality within the business object data finalize restore service. */ public class BusinessObjectDataFinalizeRestoreServiceTest extends AbstractServiceTest { @Autowired @Qualifier(value = "businessObjectDataFinalizeRestoreServiceImpl") private BusinessObjectDataFinalizeRestoreServiceImpl businessObjectDataFinalizeRestoreServiceImpl; /** * This method is to get coverage for the business object data finalize restore service methods that have an explicit annotation for transaction * propagation. */ @Test public void testBusinessObjectDataFinalizeRestoreServiceMethodsNewTransactionPropagation() { assertEquals(0, businessObjectDataFinalizeRestoreServiceImpl.getS3StorageUnitsToRestore(MAX_RESULT).size()); // Create a business object data key. BusinessObjectDataKey businessObjectDataKey = new BusinessObjectDataKey(BDEF_NAMESPACE, BDEF_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE, SUBPARTITION_VALUES, DATA_VERSION); // Create a storage unit key. StorageUnitAlternateKeyDto storageUnitKey = storageUnitHelper.createStorageUnitKey(businessObjectDataKey, STORAGE_NAME); try { businessObjectDataFinalizeRestoreServiceImpl.finalizeRestore(storageUnitKey); fail(); } catch (ObjectNotFoundException e) { assertEquals(businessObjectDataServiceTestHelper.getExpectedBusinessObjectDataNotFoundErrorMessage(businessObjectDataKey, NO_BDATA_STATUS), e.getMessage()); } } @Test public void testFinalizeRestore() throws Exception { // Create a business object data key. BusinessObjectDataKey businessObjectDataKey = new BusinessObjectDataKey(BDEF_NAMESPACE, BDEF_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE, NO_SUBPARTITION_VALUES, DATA_VERSION); // Get the expected S3 key prefix for the business object data key. String s3KeyPrefix = AbstractServiceTest .getExpectedS3KeyPrefix(businessObjectDataKey, AbstractServiceTest.DATA_PROVIDER_NAME, AbstractServiceTest.PARTITION_KEY, AbstractServiceTest.NO_SUB_PARTITION_KEYS); // Create S3FileTransferRequestParamsDto to access the S3 bucket. // Since test S3 key prefix represents a directory, we add a trailing '/' character to it. S3FileTransferRequestParamsDto s3FileTransferRequestParamsDto = S3FileTransferRequestParamsDto.builder().s3BucketName(S3_BUCKET_NAME).s3KeyPrefix(s3KeyPrefix + "/").build(); // Create database entities required for testing. BusinessObjectDataEntity businessObjectDataEntity = businessObjectDataServiceTestHelper.createDatabaseEntitiesForFinalizeRestoreTesting(businessObjectDataKey); // Get the storage unit entity. StorageUnitEntity storageUnitEntity = storageUnitDaoHelper.getStorageUnitEntity(STORAGE_NAME, businessObjectDataEntity); // Create a storage unit key. StorageUnitAlternateKeyDto storageUnitKey = storageUnitHelper.createStorageUnitKey(businessObjectDataKey, STORAGE_NAME); try { // Put relative "already restored" Glacier storage class S3 files in the S3 bucket. for (StorageFileEntity storageFileEntity : storageUnitEntity.getStorageFiles()) { ObjectMetadata metadata = new ObjectMetadata(); metadata.setHeader(Headers.STORAGE_CLASS, StorageClass.Glacier); metadata.setOngoingRestore(false); s3Operations.putObject(new PutObjectRequest(S3_BUCKET_NAME, storageFileEntity.getPath(), new ByteArrayInputStream(new byte[storageFileEntity.getFileSizeBytes().intValue()]), metadata), NO_S3_CLIENT); } // Finalize a restore of the storage unit. businessObjectDataFinalizeRestoreService.finalizeRestore(storageUnitKey); // Validate that the storage unit status is now RESTORED. assertEquals(StorageUnitStatusEntity.RESTORED, storageUnitEntity.getStatus().getCode()); // Validate that we have the S3 files at the expected S3 location. assertEquals(storageUnitEntity.getStorageFiles().size(), s3Dao.listDirectory(s3FileTransferRequestParamsDto).size()); } finally { // Delete test files from S3 storage. if (!s3Dao.listDirectory(s3FileTransferRequestParamsDto).isEmpty()) { s3Dao.deleteDirectory(s3FileTransferRequestParamsDto); } s3Operations.rollback(); } } @Test public void testFinalizeRestoreAmazonServiceException() throws Exception { // Create a business object data key. BusinessObjectDataKey businessObjectDataKey = new BusinessObjectDataKey(BDEF_NAMESPACE, BDEF_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE, NO_SUBPARTITION_VALUES, DATA_VERSION); // Get the expected S3 key prefix for the business object data key. String s3KeyPrefix = AbstractServiceTest .getExpectedS3KeyPrefix(businessObjectDataKey, AbstractServiceTest.DATA_PROVIDER_NAME, AbstractServiceTest.PARTITION_KEY, AbstractServiceTest.NO_SUB_PARTITION_KEYS); // Create S3FileTransferRequestParamsDto to access the S3 bucket. // Since test S3 key prefix represents a directory, we add a trailing '/' character to it. S3FileTransferRequestParamsDto s3FileTransferRequestParamsDto = S3FileTransferRequestParamsDto.builder().s3BucketName(S3_BUCKET_NAME).s3KeyPrefix(s3KeyPrefix + "/").build(); // Create database entities required for testing. BusinessObjectDataEntity businessObjectDataEntity = businessObjectDataServiceTestHelper.createDatabaseEntitiesForFinalizeRestoreTesting(businessObjectDataKey); // Get the storage unit entity. StorageUnitEntity storageUnitEntity = storageUnitDaoHelper.getStorageUnitEntity(STORAGE_NAME, businessObjectDataEntity); // Add a mocked S3 file name to the storage unit that would trigger an Amazon service exception when we try to get metadata for the object. storageFileDaoTestHelper .createStorageFileEntity(storageUnitEntity, String.format("%s/%s", s3KeyPrefix, MockS3OperationsImpl.MOCK_S3_FILE_NAME_SERVICE_EXCEPTION), FILE_SIZE_1_KB, ROW_COUNT); // Create a storage unit key. StorageUnitAlternateKeyDto storageUnitKey = storageUnitHelper.createStorageUnitKey(businessObjectDataKey, STORAGE_NAME); try { // Put relative "already restored" Glacier storage class S3 files in the S3 bucket. for (StorageFileEntity storageFileEntity : storageUnitEntity.getStorageFiles()) { ObjectMetadata metadata = new ObjectMetadata(); metadata.setHeader(Headers.STORAGE_CLASS, StorageClass.Glacier); metadata.setOngoingRestore(false); s3Operations.putObject(new PutObjectRequest(S3_BUCKET_NAME, storageFileEntity.getPath(), new ByteArrayInputStream(new byte[storageFileEntity.getFileSizeBytes().intValue()]), metadata), null); } // Try to finalize a restore for the storage unit when get S3 object metadata operation fails with an Amazon service exception. try { businessObjectDataFinalizeRestoreService.finalizeRestore(storageUnitKey); fail("Should throw an IllegalStateException when a get S3 object metadata operation fails."); } catch (IllegalStateException e) { assertEquals(String.format("Fail to check restore status for \"%s/%s\" key in \"%s\" bucket. " + "Reason: InternalError (Service: null; Status Code: 0; Error Code: InternalError; Request ID: null)", s3KeyPrefix, MockS3OperationsImpl.MOCK_S3_FILE_NAME_SERVICE_EXCEPTION, S3_BUCKET_NAME), e.getMessage()); } // Validate that the storage unit status is still in RESTORING state. assertEquals(StorageUnitStatusEntity.RESTORING, storageUnitEntity.getStatus().getCode()); // Validate that we have the S3 files at the expected S3 location. assertEquals(storageUnitEntity.getStorageFiles().size(), s3Dao.listDirectory(s3FileTransferRequestParamsDto).size()); } finally { // Delete test files from S3 storage. if (!s3Dao.listDirectory(s3FileTransferRequestParamsDto).isEmpty()) { s3Dao.deleteDirectory(s3FileTransferRequestParamsDto); } s3Operations.rollback(); } } @Test public void testGetS3StorageUnitsToRestore() throws Exception { // Create a list of business object data keys. List<BusinessObjectDataKey> businessObjectDataKeys = Arrays.asList( new BusinessObjectDataKey(BDEF_NAMESPACE, BDEF_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE, SUBPARTITION_VALUES, DATA_VERSION), new BusinessObjectDataKey(BDEF_NAMESPACE, BDEF_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE_2, SUBPARTITION_VALUES, DATA_VERSION)); // Create database entities required for testing. for (BusinessObjectDataKey businessObjectDataKey : businessObjectDataKeys) { storageUnitDaoTestHelper.createBusinessObjectDataEntityInRestoringState(businessObjectDataKey, STORAGE_NAME, StorageUnitStatusEntity.RESTORING); } // Select the RESTORING storage units. List<StorageUnitAlternateKeyDto> resultStorageUnitKeys = businessObjectDataFinalizeRestoreService.getS3StorageUnitsToRestore(MAX_RESULT); // Validate the results. assertEquals(businessObjectDataKeys.size(), resultStorageUnitKeys.size()); for (BusinessObjectDataKey businessObjectDataKey : businessObjectDataKeys) { assertTrue(resultStorageUnitKeys.contains(storageUnitHelper.createStorageUnitKey(businessObjectDataKey, STORAGE_NAME))); } // Try to select the RESTORING storage units with max result limit set to 1. Only one storage unit is expected to be returned. assertEquals(1, businessObjectDataFinalizeRestoreService.getS3StorageUnitsToRestore(1).size()); } }