Obtain upload lock before uploading
[sdc.git] / 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
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  * Copyright © 2021 Nokia
4  * Copyright © 2021 Nordix Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  * ============LICENSE_END=========================================================
18  * Modifications copyright (c) 2019 Nokia
19  * Modifications copyright (c) 2021 Nordix Foundation
20  * ================================================================================
21  */
22 package org.openecomp.sdcrests.vsp.rest.services;
23
24 import static javax.ws.rs.core.Response.Status.EXPECTATION_FAILED;
25 import static javax.ws.rs.core.Response.Status.NOT_ACCEPTABLE;
26 import static javax.ws.rs.core.Response.Status.NOT_FOUND;
27 import static org.openecomp.core.validation.errors.ErrorMessagesFormatBuilder.getErrorWithParameters;
28 import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT;
29 import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE;
30 import static org.openecomp.sdc.common.errors.Messages.NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST;
31 import static org.openecomp.sdc.common.errors.Messages.PACKAGE_PROCESS_ERROR;
32 import static org.openecomp.sdc.common.errors.Messages.UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING;
33 import static org.openecomp.sdcrests.vsp.rest.exception.OrchestrationTemplateCandidateUploadManagerExceptionSupplier.vspUploadAlreadyInProgress;
34
35 import java.io.ByteArrayInputStream;
36 import java.io.FileInputStream;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.nio.file.Files;
41 import java.nio.file.Path;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Optional;
48 import java.util.UUID;
49 import javax.activation.DataHandler;
50 import javax.inject.Named;
51 import javax.ws.rs.core.Response;
52 import org.apache.commons.lang3.tuple.Pair;
53 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
54 import org.openecomp.sdc.activitylog.ActivityLogManager;
55 import org.openecomp.sdc.activitylog.ActivityLogManagerFactory;
56 import org.openecomp.sdc.activitylog.dao.type.ActivityLogEntity;
57 import org.openecomp.sdc.activitylog.dao.type.ActivityType;
58 import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
59 import org.openecomp.sdc.be.csar.storage.ArtifactStorageConfig;
60 import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager;
61 import org.openecomp.sdc.be.csar.storage.PackageSizeReducer;
62 import org.openecomp.sdc.be.csar.storage.StorageFactory;
63 import org.openecomp.sdc.be.csar.storage.exception.ArtifactStorageException;
64 import org.openecomp.sdc.common.util.ValidationUtils;
65 import org.openecomp.sdc.common.utils.SdcCommon;
66 import org.openecomp.sdc.datatypes.error.ErrorLevel;
67 import org.openecomp.sdc.datatypes.error.ErrorMessage;
68 import org.openecomp.sdc.logging.api.Logger;
69 import org.openecomp.sdc.logging.api.LoggerFactory;
70 import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManager;
71 import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManagerFactory;
72 import org.openecomp.sdc.vendorsoftwareproduct.VendorSoftwareProductManager;
73 import org.openecomp.sdc.vendorsoftwareproduct.VspManagerFactory;
74 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.VspDetails;
75 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.VspUploadStatus;
76 import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.OnboardingPackageProcessor;
77 import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.validation.CnfPackageValidator;
78 import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackageInfo;
79 import org.openecomp.sdc.vendorsoftwareproduct.types.OrchestrationTemplateActionResponse;
80 import org.openecomp.sdc.vendorsoftwareproduct.types.UploadFileResponse;
81 import org.openecomp.sdc.vendorsoftwareproduct.types.ValidationResponse;
82 import org.openecomp.sdc.vendorsoftwareproduct.types.candidateheat.FilesDataStructure;
83 import org.openecomp.sdc.versioning.dao.types.Version;
84 import org.openecomp.sdcrests.vendorsoftwareproducts.types.FileDataStructureDto;
85 import org.openecomp.sdcrests.vendorsoftwareproducts.types.OrchestrationTemplateActionResponseDto;
86 import org.openecomp.sdcrests.vendorsoftwareproducts.types.UploadFileResponseDto;
87 import org.openecomp.sdcrests.vendorsoftwareproducts.types.ValidationResponseDto;
88 import org.openecomp.sdcrests.vendorsoftwareproducts.types.VspUploadStatusDto;
89 import org.openecomp.sdcrests.vsp.rest.OrchestrationTemplateCandidate;
90 import org.openecomp.sdcrests.vsp.rest.mapping.MapFilesDataStructureToDto;
91 import org.openecomp.sdcrests.vsp.rest.mapping.MapUploadFileResponseToUploadFileResponseDto;
92 import org.openecomp.sdcrests.vsp.rest.mapping.MapValidationResponseToDto;
93 import org.springframework.beans.factory.annotation.Autowired;
94 import org.springframework.context.annotation.Scope;
95 import org.springframework.stereotype.Service;
96
97 @Named
98 @Service("orchestrationTemplateCandidate")
99 @Scope(value = "prototype")
100 public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplateCandidate {
101
102     private static final Logger LOGGER = LoggerFactory.getLogger(OrchestrationTemplateCandidateImpl.class);
103     private final OrchestrationTemplateCandidateManager candidateManager;
104     private final VendorSoftwareProductManager vendorSoftwareProductManager;
105     private final ActivityLogManager activityLogManager;
106     private final ArtifactStorageManager artifactStorageManager;
107     private final StorageFactory storageFactory;
108     private final PackageSizeReducer packageSizeReducer;
109     private final OrchestrationTemplateCandidateUploadManager orchestrationTemplateCandidateUploadManager;
110
111     @Autowired
112     public OrchestrationTemplateCandidateImpl(final OrchestrationTemplateCandidateUploadManager orchestrationTemplateCandidateUploadManager) {
113         this.candidateManager = OrchestrationTemplateCandidateManagerFactory.getInstance().createInterface();
114         this.vendorSoftwareProductManager = VspManagerFactory.getInstance().createInterface();
115         this.activityLogManager = ActivityLogManagerFactory.getInstance().createInterface();
116         LOGGER.info("Instantiating artifactStorageManager");
117         this.storageFactory = new StorageFactory();
118         this.artifactStorageManager = storageFactory.createArtifactStorageManager();
119         LOGGER.info("Instantiating packageSizeReducer");
120         this.packageSizeReducer = storageFactory.createPackageSizeReducer().orElse(null);
121         this.orchestrationTemplateCandidateUploadManager = orchestrationTemplateCandidateUploadManager;
122     }
123
124     // Constructor used in test to avoid mock static
125     public OrchestrationTemplateCandidateImpl(final OrchestrationTemplateCandidateManager candidateManager,
126                                               final VendorSoftwareProductManager vendorSoftwareProductManager,
127                                               final ActivityLogManager activityLogManager,
128                                               final ArtifactStorageManager artifactStorageManager,
129                                               final PackageSizeReducer packageSizeReducer,
130                                               final OrchestrationTemplateCandidateUploadManager orchestrationTemplateCandidateUploadManager) {
131         this.candidateManager = candidateManager;
132         this.vendorSoftwareProductManager = vendorSoftwareProductManager;
133         this.activityLogManager = activityLogManager;
134         this.artifactStorageManager = artifactStorageManager;
135         this.storageFactory = new StorageFactory();
136         this.packageSizeReducer = packageSizeReducer;
137         this.orchestrationTemplateCandidateUploadManager = orchestrationTemplateCandidateUploadManager;
138     }
139
140     @Override
141     public Response upload(String vspId, String versionId, final Attachment fileToUpload, final String user) {
142         LOGGER.debug("STARTED -> OrchestrationTemplateCandidateImpl.upload");
143         vspId = ValidationUtils.sanitizeInputString(vspId);
144         versionId = ValidationUtils.sanitizeInputString(versionId);
145         final Response response;
146         VspUploadStatusDto vspUploadStatus = null;
147         try {
148             vspUploadStatus = getVspUploadStatus(vspId, versionId, user);
149
150             if (vspUploadStatus.getStatus() != VspUploadStatus.UPLOADING) {
151                 throw vspUploadAlreadyInProgress(vspId, versionId).get();
152             }
153             final byte[] fileToUploadBytes;
154             final DataHandler dataHandler = fileToUpload.getDataHandler();
155             final var filename = ValidationUtils.sanitizeInputString(dataHandler.getName());
156             ArtifactInfo artifactInfo = null;
157             final ArtifactStorageManager artifactStorageManager = storageFactory.createArtifactStorageManager();
158             if (artifactStorageManager.isEnabled()) {
159                 artifactInfo = handleArtifactStorage(vspId, versionId, filename, dataHandler);
160                 fileToUploadBytes = artifactInfo.getBytes();
161             } else {
162                 fileToUploadBytes = fileToUpload.getObject(byte[].class);
163             }
164
165             vspUploadStatus = orchestrationTemplateCandidateUploadManager.putUploadInValidation(vspId, versionId, user);
166             final var onboardingPackageProcessor =
167                 new OnboardingPackageProcessor(filename, fileToUploadBytes, new CnfPackageValidator(), artifactInfo);
168             final ErrorMessage[] errorMessages = onboardingPackageProcessor.getErrorMessages().toArray(new ErrorMessage[0]);
169             if (onboardingPackageProcessor.hasErrors()) {
170                 orchestrationTemplateCandidateUploadManager
171                     .putUploadAsFinished(vspId, versionId, vspUploadStatus.getLockId(), VspUploadStatus.ERROR, user);
172                 return Response.status(NOT_ACCEPTABLE).entity(buildUploadResponseWithError(errorMessages)).build();
173             }
174             final var onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null);
175             if (onboardPackageInfo == null) {
176                 final UploadFileResponseDto uploadFileResponseDto = buildUploadResponseWithError(
177                     new ErrorMessage(ErrorLevel.ERROR, PACKAGE_PROCESS_ERROR.formatMessage(filename)));
178                 orchestrationTemplateCandidateUploadManager
179                     .putUploadAsFinished(vspId, versionId, vspUploadStatus.getLockId(), VspUploadStatus.ERROR, user);
180                 return Response.ok(uploadFileResponseDto).build();
181             }
182             final var version = new Version(versionId);
183             final var vspDetails = vendorSoftwareProductManager.getVsp(vspId, version);
184             vspUploadStatus = orchestrationTemplateCandidateUploadManager.putUploadInProcessing(vspId, versionId, user);
185             response = processOnboardPackage(onboardPackageInfo, vspDetails, errorMessages);
186             final UploadFileResponseDto entity = (UploadFileResponseDto) response.getEntity();
187             if (artifactStorageManager.isEnabled()) {
188                 if (entity.getErrors().isEmpty()) {
189                     artifactStorageManager.put(vspId, versionId + ".reduced", new ByteArrayInputStream(fileToUploadBytes));
190                 } else {
191                     artifactStorageManager.delete(artifactInfo);
192                 }
193             }
194             orchestrationTemplateCandidateUploadManager
195                 .putUploadAsFinished(vspId, versionId, vspUploadStatus.getLockId(), VspUploadStatus.SUCCESS, user);
196         } catch (final Exception ex) {
197             if (vspUploadStatus != null) {
198                 orchestrationTemplateCandidateUploadManager
199                     .putUploadAsFinished(vspId, versionId, vspUploadStatus.getLockId(), VspUploadStatus.ERROR, user);
200             }
201             throw ex;
202         }
203         LOGGER.debug("FINISHED -> OrchestrationTemplateCandidateImpl.upload");
204         return response;
205     }
206
207     private VspUploadStatusDto getVspUploadStatus(final String vspId, final String versionId, final String user) {
208         final Optional<VspUploadStatusDto> vspUploadStatusOpt =
209             orchestrationTemplateCandidateUploadManager.findLatestStatus(vspId, versionId, user);
210         if (vspUploadStatusOpt.isEmpty() || vspUploadStatusOpt.get().isComplete()) {
211             return orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user);
212         }
213
214         return vspUploadStatusOpt.get();
215     }
216
217     private ArtifactInfo handleArtifactStorage(final String vspId, final String versionId, final String filename,
218                                                final DataHandler artifactDataHandler) {
219         final Path tempArtifactPath;
220         try {
221             final ArtifactStorageConfig storageConfiguration = artifactStorageManager.getStorageConfiguration();
222
223             final Path folder = Path.of(storageConfiguration.getTempPath()).resolve(vspId).resolve(versionId);
224             tempArtifactPath = folder.resolve(UUID.randomUUID().toString());
225             Files.createDirectories(folder);
226             LOGGER.debug("STARTED -> Transfer to '{}'", tempArtifactPath.toString());
227             try (final InputStream packageInputStream = artifactDataHandler.getInputStream();
228                 final var fileOutputStream = new FileOutputStream(tempArtifactPath.toFile())) {
229                 packageInputStream.transferTo(fileOutputStream);
230             }
231             LOGGER.debug("FINISHED -> Transfer to '{}'", tempArtifactPath.toString());
232         } catch (final Exception e) {
233             throw new ArtifactStorageException(UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING.formatMessage(filename));
234         }
235         final ArtifactInfo artifactInfo;
236         try (final InputStream inputStream = new FileInputStream(tempArtifactPath.toFile())) {
237             artifactInfo = artifactStorageManager.upload(vspId, versionId, inputStream);
238         } catch (final Exception e) {
239             LOGGER.error("Package Size Reducer not configured", e);
240             throw new ArtifactStorageException(ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT.formatMessage(filename));
241         }
242         try {
243             LOGGER.debug("STARTED -> reducing '{}'", tempArtifactPath.toString());
244             artifactInfo.setBytes(packageSizeReducer.reduce(tempArtifactPath));
245             LOGGER.debug("FINISHED -> reducing '{}'", tempArtifactPath.toString());
246             Files.delete(tempArtifactPath);
247         } catch (final Exception e) {
248             LOGGER.error("Package Size Reducer not configured", e);
249             throw new ArtifactStorageException(ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(filename));
250         }
251         return artifactInfo;
252     }
253
254     private Response processOnboardPackage(final OnboardPackageInfo onboardPackageInfo, final VspDetails vspDetails,
255                                            final ErrorMessage... errorMessages) {
256         final UploadFileResponse uploadFileResponse = candidateManager.upload(vspDetails, onboardPackageInfo);
257         final UploadFileResponseDto uploadFileResponseDto = new MapUploadFileResponseToUploadFileResponseDto()
258             .applyMapping(uploadFileResponse, UploadFileResponseDto.class);
259         if (errorMessages.length > 0) {
260             uploadFileResponseDto.setErrors(getErrorMap(errorMessages));
261         }
262         return Response.ok(uploadFileResponseDto).build();
263     }
264
265     private Map<String, List<ErrorMessage>> getErrorMap(ErrorMessage[] errorMessages) {
266         final Map<String, List<ErrorMessage>> errorMap = new HashMap<>();
267         final List<ErrorMessage> errorMessageList = new ArrayList<>();
268         Collections.addAll(errorMessageList, errorMessages);
269         errorMap.put(SdcCommon.UPLOAD_FILE, errorMessageList);
270         return errorMap;
271     }
272
273     private UploadFileResponseDto buildUploadResponseWithError(final ErrorMessage... errorMessages) {
274         final UploadFileResponseDto uploadFileResponseDto = new UploadFileResponseDto();
275         uploadFileResponseDto.setErrors(getErrorMap(errorMessages));
276         return uploadFileResponseDto;
277     }
278
279     @Override
280     public Response get(String vspId, String versionId, String user) throws IOException {
281         Optional<Pair<String, byte[]>> zipFile = candidateManager.get(vspId, new Version(versionId));
282         String fileName;
283         if (zipFile.isPresent()) {
284             fileName = "Candidate." + zipFile.get().getLeft();
285         } else {
286             zipFile = vendorSoftwareProductManager.get(vspId, new Version((versionId)));
287             if (zipFile.isEmpty()) {
288                 ErrorMessage errorMessage = new ErrorMessage(ErrorLevel.ERROR,
289                     getErrorWithParameters(NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST.getErrorMessage(), ""));
290                 LOGGER.error(errorMessage.getMessage());
291                 return Response.status(NOT_FOUND).build();
292             }
293             fileName = "Processed." + zipFile.get().getLeft();
294         }
295         Response.ResponseBuilder response = Response.ok(zipFile.get().getRight());
296         response.header("Content-Disposition", "attachment; filename=" + fileName);
297         return response.build();
298     }
299
300     @Override
301     public Response abort(String vspId, String versionId) {
302         candidateManager.abort(vspId, new Version(versionId));
303         return Response.ok().build();
304     }
305
306     @Override
307     public Response process(String vspId, String versionId, String user) {
308         Version version = new Version(versionId);
309         OrchestrationTemplateActionResponse response = candidateManager.process(vspId, version);
310         activityLogManager.logActivity(new ActivityLogEntity(vspId, version, ActivityType.Upload_Network_Package, user, true, "", ""));
311         OrchestrationTemplateActionResponseDto responseDto = copyOrchestrationTemplateActionResponseToDto(response);
312         return Response.ok(responseDto).build();
313     }
314
315     @Override
316     public Response updateFilesDataStructure(String vspId, String versionId, FileDataStructureDto fileDataStructureDto, String user) {
317         FilesDataStructure fileDataStructure = copyFilesDataStructureDtoToFilesDataStructure(fileDataStructureDto);
318         ValidationResponse response = candidateManager.updateFilesDataStructure(vspId, new Version(versionId), fileDataStructure);
319         if (!response.isValid()) {
320             return Response.status(EXPECTATION_FAILED)
321                 .entity(new MapValidationResponseToDto().applyMapping(response, ValidationResponseDto.class))
322                 .build();
323         }
324         return Response.ok(fileDataStructureDto).build();
325     }
326
327     @Override
328     public Response getFilesDataStructure(String vspId, String versionId, String user) {
329         Optional<FilesDataStructure> filesDataStructure = candidateManager.getFilesDataStructure(vspId, new Version(versionId));
330         if (filesDataStructure.isEmpty()) {
331             filesDataStructure = vendorSoftwareProductManager.getOrchestrationTemplateStructure(vspId, new Version(versionId));
332         }
333         FileDataStructureDto fileDataStructureDto = filesDataStructure
334             .map(dataStructure -> new MapFilesDataStructureToDto().applyMapping(dataStructure, FileDataStructureDto.class))
335             .orElse(new FileDataStructureDto());
336         return Response.ok(fileDataStructureDto).build();
337     }
338
339     private OrchestrationTemplateActionResponseDto copyOrchestrationTemplateActionResponseToDto(OrchestrationTemplateActionResponse response) {
340         OrchestrationTemplateActionResponseDto result = new OrchestrationTemplateActionResponseDto();
341         result.setErrors(response.getErrors());
342         result.setFileNames(response.getFileNames());
343         result.setStatus(response.getStatus());
344         return result;
345     }
346
347     private FilesDataStructure copyFilesDataStructureDtoToFilesDataStructure(FileDataStructureDto fileDataStructureDto) {
348         FilesDataStructure filesDataStructure = new FilesDataStructure();
349         filesDataStructure.setArtifacts(fileDataStructureDto.getArtifacts());
350         filesDataStructure.setModules(fileDataStructureDto.getModules());
351         filesDataStructure.setNested(fileDataStructureDto.getNested());
352         filesDataStructure.setUnassigned(fileDataStructureDto.getUnassigned());
353         return filesDataStructure;
354     }
355 }