From 707fb6d83819058d5736b2dc38bea3c2d9e07a2d Mon Sep 17 00:00:00 2001 From: vasraz Date: Fri, 8 Oct 2021 14:48:08 +0100 Subject: Large csar handling - object store Change-Id: I4e88bd7bfcc1fdbc93d67da2682f2e873ba243c6 Signed-off-by: Vasyl Razinkov Issue-ID: SDC-3754 --- .../OrchestrationTemplateCandidateImpl.java | 87 ++++++++++------------ .../OrchestrationTemplateCandidateImplTest.java | 36 +++++---- 2 files changed, 60 insertions(+), 63 deletions(-) (limited to 'openecomp-be/api') diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java index 23930ed640..6fe7f9dd0a 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java @@ -28,13 +28,14 @@ import static javax.ws.rs.core.Response.Status.NOT_FOUND; import static org.openecomp.core.validation.errors.ErrorMessagesFormatBuilder.getErrorWithParameters; import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT; import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE; -import static org.openecomp.sdc.common.errors.Messages.EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH; import static org.openecomp.sdc.common.errors.Messages.NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST; import static org.openecomp.sdc.common.errors.Messages.PACKAGE_PROCESS_ERROR; import static org.openecomp.sdc.common.errors.Messages.UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -42,8 +43,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.UUID; +import javax.activation.DataHandler; import javax.inject.Named; import javax.ws.rs.core.Response; import org.apache.commons.lang3.tuple.Pair; @@ -55,13 +56,8 @@ import org.openecomp.sdc.activitylog.dao.type.ActivityType; import org.openecomp.sdc.be.csar.storage.ArtifactInfo; import org.openecomp.sdc.be.csar.storage.ArtifactStorageConfig; import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager; -import org.openecomp.sdc.be.csar.storage.CsarPackageReducerConfiguration; -import org.openecomp.sdc.be.csar.storage.CsarSizeReducer; import org.openecomp.sdc.be.csar.storage.PackageSizeReducer; -import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageConfig; -import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageManager; -import org.openecomp.sdc.be.exception.BusinessException; -import org.openecomp.sdc.common.CommonConfigurationManager; +import org.openecomp.sdc.be.csar.storage.StorageFactory; import org.openecomp.sdc.common.util.ValidationUtils; import org.openecomp.sdc.common.utils.SdcCommon; import org.openecomp.sdc.datatypes.error.ErrorLevel; @@ -98,7 +94,6 @@ import org.springframework.stereotype.Service; public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplateCandidate { private static final Logger LOGGER = LoggerFactory.getLogger(OrchestrationTemplateCandidateImpl.class); - private static final String EXTERNAL_CSAR_STORE = "externalCsarStore"; private final OrchestrationTemplateCandidateManager candidateManager; private final VendorSoftwareProductManager vendorSoftwareProductManager; private final ActivityLogManager activityLogManager; @@ -110,9 +105,10 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate this.vendorSoftwareProductManager = VspManagerFactory.getInstance().createInterface(); this.activityLogManager = ActivityLogManagerFactory.getInstance().createInterface(); LOGGER.info("Instantiating artifactStorageManager"); - this.artifactStorageManager = new PersistentVolumeArtifactStorageManager(readArtifactStorageConfiguration()); + final StorageFactory storageFactory = new StorageFactory(); + this.artifactStorageManager = storageFactory.createArtifactStorageManager(); LOGGER.info("Instantiating packageSizeReducer"); - this.packageSizeReducer = new CsarSizeReducer(readPackageReducerConfiguration()); + this.packageSizeReducer = storageFactory.createPackageSizeReducer().orElse(null); } // Constructor used in test to avoid mock static @@ -128,56 +124,45 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate this.packageSizeReducer = packageSizeReducer; } - private CsarPackageReducerConfiguration readPackageReducerConfiguration() { - final var commonConfigurationManager = CommonConfigurationManager.getInstance(); - final List foldersToStrip = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "foldersToStrip", new ArrayList<>()); - final int sizeLimit = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "sizeLimit", 1000000); - final int thresholdEntries = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "thresholdEntries", 10000); - LOGGER.info("Folders to strip: '{}'", String.join(", ", foldersToStrip)); - final Set foldersToStripPathSet = foldersToStrip.stream().map(Path::of).collect(Collectors.toSet()); - return new CsarPackageReducerConfiguration(foldersToStripPathSet, sizeLimit, thresholdEntries); - } - - private ArtifactStorageConfig readArtifactStorageConfiguration() { - final var commonConfigurationManager = CommonConfigurationManager.getInstance(); - final boolean isEnabled = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "storeCsarsExternally", false); - LOGGER.info("ArtifactConfig.isEnabled: '{}'", isEnabled); - final String storagePathString = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "fullPath", null); - LOGGER.info("ArtifactConfig.storagePath: '{}'", storagePathString); - if (isEnabled && storagePathString == null) { - throw new OrchestrationTemplateCandidateException(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH.getErrorMessage()); - } - final var storagePath = storagePathString == null ? null : Path.of(storagePathString); - return new PersistentVolumeArtifactStorageConfig(isEnabled, storagePath); - } - @Override public Response upload(String vspId, String versionId, final Attachment fileToUpload, final String user) { vspId = ValidationUtils.sanitizeInputString(vspId); versionId = ValidationUtils.sanitizeInputString(versionId); final byte[] fileToUploadBytes; - final var filename = ValidationUtils.sanitizeInputString(fileToUpload.getDataHandler().getName()); + final DataHandler dataHandler = fileToUpload.getDataHandler(); + final var filename = ValidationUtils.sanitizeInputString(dataHandler.getName()); ArtifactInfo artifactInfo = null; if (artifactStorageManager.isEnabled()) { - final InputStream packageInputStream; + final Path tempArtifactPath; try { - packageInputStream = fileToUpload.getDataHandler().getInputStream(); - } catch (final IOException e) { + final ArtifactStorageConfig storageConfiguration = artifactStorageManager.getStorageConfiguration(); + + final Path folder = Path.of(storageConfiguration.getTempPath()).resolve(vspId).resolve(versionId); + tempArtifactPath = folder.resolve(UUID.randomUUID().toString()); + Files.createDirectories(folder); + try (final InputStream packageInputStream = dataHandler.getInputStream(); + final var fileOutputStream = new FileOutputStream(tempArtifactPath.toFile())) { + packageInputStream.transferTo(fileOutputStream); + } + } catch (final Exception e) { return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError( new ErrorMessage(ErrorLevel.ERROR, UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING.formatMessage(filename)))).build(); } - try { - artifactInfo = artifactStorageManager.upload(vspId, versionId, packageInputStream); - } catch (final BusinessException e) { + try (final InputStream inputStream = Files.newInputStream(tempArtifactPath)) { + artifactInfo = artifactStorageManager.upload(vspId, versionId, inputStream); + } catch (final Exception e) { + LOGGER.error("Package Size Reducer not configured", e); return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError( new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT.formatMessage(filename)))).build(); } try { - fileToUploadBytes = packageSizeReducer.reduce(artifactInfo.getPath()); - } catch (final BusinessException e) { + fileToUploadBytes = packageSizeReducer.reduce(tempArtifactPath); + Files.delete(tempArtifactPath); + } catch (final Exception e) { + LOGGER.error("Package Size Reducer not configured", e); return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError( - new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(artifactInfo.getPath())))) - .build(); + new ErrorMessage(ErrorLevel.ERROR, + ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(tempArtifactPath.toString())))).build(); } } else { fileToUploadBytes = fileToUpload.getObject(byte[].class); @@ -192,12 +177,16 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate if (onboardPackageInfo == null) { final UploadFileResponseDto uploadFileResponseDto = buildUploadResponseWithError( new ErrorMessage(ErrorLevel.ERROR, PACKAGE_PROCESS_ERROR.formatMessage(filename))); - return Response.ok(uploadFileResponseDto) - .build(); + return Response.ok(uploadFileResponseDto).build(); } final var version = new Version(versionId); final var vspDetails = vendorSoftwareProductManager.getVsp(vspId, version); - return processOnboardPackage(onboardPackageInfo, vspDetails, errorMessages); + final Response response = processOnboardPackage(onboardPackageInfo, vspDetails, errorMessages); + final UploadFileResponseDto entity = (UploadFileResponseDto) response.getEntity(); + if (artifactStorageManager.isEnabled() && !entity.getErrors().isEmpty()) { + artifactStorageManager.delete(artifactInfo); + } + return response; } private Response processOnboardPackage(final OnboardPackageInfo onboardPackageInfo, final VspDetails vspDetails, diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java index edf29b75c5..2d2c30865a 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java @@ -32,6 +32,8 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -49,14 +51,18 @@ import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum; import org.openecomp.sdc.activitylog.ActivityLogManager; import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager; +import org.openecomp.sdc.be.csar.storage.MinIoArtifactInfo; +import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig; +import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig.Credentials; +import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig.EndPoint; import org.openecomp.sdc.be.csar.storage.PackageSizeReducer; -import org.openecomp.sdc.be.csar.storage.PersistentStorageArtifactInfo; import org.openecomp.sdc.logging.api.Logger; import org.openecomp.sdc.logging.api.LoggerFactory; import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManager; @@ -88,6 +94,7 @@ class OrchestrationTemplateCandidateImplTest { private ArtifactStorageManager artifactStorageManager; @Mock private PackageSizeReducer packageSizeReducer; + @InjectMocks private OrchestrationTemplateCandidateImpl orchestrationTemplateCandidate; @BeforeEach @@ -132,20 +139,15 @@ class OrchestrationTemplateCandidateImplTest { ArgumentMatchers.eq(candidateId), ArgumentMatchers.any())).thenReturn(Optional.of(fds)); - orchestrationTemplateCandidate = - new OrchestrationTemplateCandidateImpl(candidateManager, vendorSoftwareProductManager, activityLogManager, - artifactStorageManager, packageSizeReducer); - } catch (Exception e) { logger.error(e.getMessage(), e); } } @Test - void uploadSignedTest() { + void uploadSignedTest() throws IOException { Response response = orchestrationTemplateCandidate - .upload("1", "1", mockAttachment("filename.zip", this.getClass().getResource("/files/sample-signed.zip")), - "1"); + .upload("1", "1", mockAttachment("filename.zip", this.getClass().getResource("/files/sample-signed.zip")), user); assertEquals(Status.OK.getStatusCode(), response.getStatus()); assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } @@ -153,7 +155,7 @@ class OrchestrationTemplateCandidateImplTest { @Test void uploadNotSignedTest() throws IOException { Response response = orchestrationTemplateCandidate.upload("1", "1", - mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), "1"); + mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), user); assertEquals(Status.OK.getStatusCode(), response.getStatus()); assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } @@ -161,23 +163,29 @@ class OrchestrationTemplateCandidateImplTest { @Test void uploadNotSignedArtifactStorageManagerIsEnabledTest() throws IOException { when(artifactStorageManager.isEnabled()).thenReturn(true); + when(artifactStorageManager.getStorageConfiguration()).thenReturn( + new MinIoStorageArtifactStorageConfig(true, new EndPoint("host", 9000, false), new Credentials("accessKey", "secretKey"), "tempPath")); + final Path path = Path.of("src/test/resources/files/sample-not-signed.csar"); - when(artifactStorageManager.upload(anyString(), anyString(), any())).thenReturn(new PersistentStorageArtifactInfo(path)); + when(artifactStorageManager.upload(anyString(), anyString(), any())).thenReturn(new MinIoArtifactInfo("vspId", "name")); final byte[] bytes = Files.readAllBytes(path); when(packageSizeReducer.reduce(any())).thenReturn(bytes); Response response = orchestrationTemplateCandidate.upload("1", "1", - mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), "1"); + mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), user); assertEquals(Status.OK.getStatusCode(), response.getStatus()); assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } - private Attachment mockAttachment(final String fileName, final URL fileToUpload) { + private Attachment mockAttachment(final String fileName, final URL fileToUpload) throws IOException { final Attachment attachment = Mockito.mock(Attachment.class); + final InputStream inputStream = Mockito.mock(InputStream.class); when(attachment.getContentDisposition()).thenReturn(new ContentDisposition("test")); final DataHandler dataHandler = Mockito.mock(DataHandler.class); when(dataHandler.getName()).thenReturn(fileName); when(attachment.getDataHandler()).thenReturn(dataHandler); + when(dataHandler.getInputStream()).thenReturn(inputStream); + when(inputStream.transferTo(any(OutputStream.class))).thenReturn(0L); byte[] bytes = "upload package Test".getBytes(); if (Objects.nonNull(fileToUpload)) { try { @@ -192,9 +200,9 @@ class OrchestrationTemplateCandidateImplTest { } @Test - void uploadSignNotValidTest() { + void uploadSignNotValidTest() throws IOException { Response response = orchestrationTemplateCandidate - .upload("1", "1", mockAttachment("filename.zip", null), "1"); + .upload("1", "1", mockAttachment("filename.zip", null), user); assertEquals(Status.NOT_ACCEPTABLE.getStatusCode(), response.getStatus()); assertFalse(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } -- cgit 1.2.3-korg