Identify SOL004 packages
[sdc.git] / openecomp-be / lib / openecomp-sdc-vendor-software-product-lib / openecomp-sdc-vendor-software-product-core / src / main / java / org / openecomp / sdc / vendorsoftwareproduct / services / impl / etsi / ETSIServiceImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2019, Nordix Foundation. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.openecomp.sdc.vendorsoftwareproduct.services.impl.etsi;
21
22 import static org.openecomp.sdc.tosca.csar.CSARConstants.ARTIFACTS_FOLDER;
23 import static org.openecomp.sdc.tosca.csar.CSARConstants.ETSI_VERSION_2_6_1;
24 import static org.openecomp.sdc.tosca.csar.CSARConstants.MAIN_SERVICE_TEMPLATE_MF_FILE_NAME;
25 import static org.openecomp.sdc.tosca.csar.CSARConstants.MANIFEST_PNF_METADATA;
26 import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_META_ORIG_PATH_FILE_NAME;
27 import static org.openecomp.sdc.tosca.csar.ManifestTokenType.COMPATIBLE_SPECIFICATION_VERSIONS;
28 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntryVersion251.ENTRY_MANIFEST;
29 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntryVersion261.ENTRY_DEFINITIONS;
30 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntryVersion261.ETSI_ENTRY_CHANGE_LOG;
31 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntryVersion261.ETSI_ENTRY_MANIFEST;
32 import static org.openecomp.sdc.tosca.csar.ToscaMetadataFileInfo.TOSCA_META_PATH_FILE_NAME;
33
34 import com.vdurmont.semver4j.Semver;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.nio.file.Path;
38 import java.nio.file.Paths;
39 import java.util.Arrays;
40 import java.util.Comparator;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Objects;
45 import java.util.Optional;
46 import java.util.stream.Collectors;
47 import org.apache.commons.collections.MapUtils;
48 import org.onap.sdc.tosca.datatypes.model.ServiceTemplate;
49 import org.onap.sdc.tosca.services.YamlUtil;
50 import org.openecomp.core.utilities.file.FileContentHandler;
51 import org.openecomp.sdc.be.config.NonManoArtifactType;
52 import org.openecomp.sdc.be.config.NonManoConfiguration;
53 import org.openecomp.sdc.be.config.NonManoConfigurationManager;
54 import org.openecomp.sdc.be.config.NonManoFolderType;
55 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
56 import org.openecomp.sdc.common.CommonConfigurationManager;
57 import org.openecomp.sdc.logging.api.Logger;
58 import org.openecomp.sdc.logging.api.LoggerFactory;
59 import org.openecomp.sdc.tosca.csar.Manifest;
60 import org.openecomp.sdc.tosca.csar.OnboardingToscaMetadata;
61 import org.openecomp.sdc.tosca.csar.SOL004ManifestOnboarding;
62 import org.openecomp.sdc.tosca.csar.ToscaMetadata;
63 import org.openecomp.sdc.tosca.datatypes.ToscaServiceModel;
64
65 public class ETSIServiceImpl implements ETSIService {
66
67     private static final Logger LOGGER = LoggerFactory.getLogger(ETSIServiceImpl.class);
68     private final NonManoConfiguration nonManoConfiguration;
69     private final String ONAP_CSAR = "onap_csar";
70
71     public ETSIServiceImpl() {
72         nonManoConfiguration = NonManoConfigurationManager.getInstance().getNonManoConfiguration();
73     }
74
75     public ETSIServiceImpl(final NonManoConfiguration nonManoConfiguration) {
76         this.nonManoConfiguration = nonManoConfiguration;
77     }
78
79     @Override
80     public boolean hasEtsiSol261Metadata(FileContentHandler handler) throws IOException {
81         final Map<String, byte[]> templates = handler.getFiles();
82         return isMetaFilePresent(templates) && hasMetaMandatoryEntries(getMetadata(handler));
83     }
84
85     @Override
86     public boolean isEtsiPackage(final FileContentHandler fileContentHandler) throws IOException {
87         return hasEtsiSol261Metadata(fileContentHandler) || !hasOnapCsarMetadata(fileContentHandler)
88             && !ONAP_CSAR.equalsIgnoreCase(getDefaultCsarFormat());
89     }
90
91     private boolean hasOnapCsarMetadata(final FileContentHandler fileContentHandler) throws IOException {
92         if (fileContentHandler.containsFile(TOSCA_META_PATH_FILE_NAME)){
93             final ToscaMetadata metadata =
94                 OnboardingToscaMetadata.parseToscaMetadataFile(fileContentHandler.getFileContentAsStream(TOSCA_META_PATH_FILE_NAME));
95             return metadata.hasEntry(ONAP_CSAR);
96         }
97         return false;
98     }
99
100     private String getDefaultCsarFormat() {
101         return CommonConfigurationManager.getInstance().getConfigValue("csarFormat", "default", ONAP_CSAR);
102     }
103
104     @Override
105     public Optional<Map<String, Path>> moveNonManoFileToArtifactFolder(final FileContentHandler handler) throws IOException {
106         final Manifest manifest = loadManifest(handler);
107         final Path originalManifestPath;
108         try {
109             originalManifestPath = getOriginalManifestPath(handler);
110         } catch (final IOException ex) {
111             if (LOGGER.isErrorEnabled()) {
112                 LOGGER.error("An error occurred while getting the original manifest path", ex);
113             }
114             throw ex;
115         }
116         final Map<String, Path> fromToPathMap = new HashMap<>();
117         final Map<String, NonManoFolderType> nonManoKeyFolderMapping = nonManoConfiguration.getNonManoKeyFolderMapping();
118         manifest.getNonManoSources().entrySet().stream()
119             .filter(manifestNonManoSourceEntry -> nonManoKeyFolderMapping.containsKey(manifestNonManoSourceEntry.getKey()))
120             .forEach(manifestNonManoSourceEntry -> {
121                 final NonManoFolderType nonManoFolderType = nonManoKeyFolderMapping.get(manifestNonManoSourceEntry.getKey());
122                 final List<String> nonManoFileList = manifestNonManoSourceEntry.getValue();
123                 final Map<String, Path> actualFromToPathMap = nonManoFileList.stream().map(nonManoFilePath -> {
124                     final Path normalizedFilePath = resolveNonManoFilePath(originalManifestPath, nonManoFilePath);
125                     final Optional<Path> changedPath = updateNonManoPathInHandler(handler, nonManoFolderType, normalizedFilePath);
126                     if (changedPath.isPresent()) {
127                         final Map<String, Path> fromAndToPathMap = new HashMap<>();
128                         fromAndToPathMap.put(nonManoFilePath, Paths.get(ARTIFACTS_FOLDER).resolve(changedPath.get()));
129                         return fromAndToPathMap;
130                     }
131                     return null;
132                 }).filter(Objects::nonNull).collect(Collectors.toMap(fromToPathEntry -> fromToPathEntry.keySet().iterator().next(),
133                     fromToPathEntry -> fromToPathEntry.values().iterator().next()));
134                 fromToPathMap.putAll(actualFromToPathMap);
135             });
136         return MapUtils.isEmpty(fromToPathMap) ? Optional.empty() : Optional.of(fromToPathMap);
137     }
138
139     /**
140      * Resolves the non mano file path based on the original manifest path of the onboarded package.
141      *
142      * @param originalManifestPath The original path from the onboarded package manifest
143      * @param nonManoFilePath      The non mano file path defined in the manifest
144      * @return The resolved and normalized non mano path.
145      */
146     private Path resolveNonManoFilePath(final Path originalManifestPath, final String nonManoFilePath) {
147         return originalManifestPath.resolve(Paths.get(nonManoFilePath)).normalize();
148     }
149
150     /**
151      * Updates the non mano file path in the package file handler based on the non mano type.
152      *
153      * @param handler                 The package file handler
154      * @param nonManoFolderType       The Non Mano type of the file to update
155      * @param nonManoOriginalFilePath The Non Mano file original path
156      * @return The new file path if it was updated in the package file handler, otherwise empty.
157      */
158     private Optional<Path> updateNonManoPathInHandler(final FileContentHandler handler, final NonManoFolderType nonManoFolderType,
159                                                       final Path nonManoOriginalFilePath) {
160         final Path fixedSourcePath = fixNonManoPath(nonManoOriginalFilePath);
161         if (handler.containsFile(fixedSourcePath.toString())) {
162             final Path newNonManoPath = Paths
163                 .get(nonManoFolderType.getType(), nonManoFolderType.getLocation(), fixedSourcePath.getFileName().toString());
164             if (!handler.containsFile(newNonManoPath.toString())) {
165                 handler.addFile(newNonManoPath.toString(), handler.remove(fixedSourcePath.toString()));
166                 return Optional.of(newNonManoPath);
167             }
168         }
169         return Optional.empty();
170     }
171
172     /**
173      * Fix the original non mano file path to the ONAP package file path.
174      * <p>
175      * Non mano artifacts that were inside the {@link org.openecomp.sdc.tosca.csar.CSARConstants#ARTIFACTS_FOLDER} path are not moved when parsed to
176      * ONAP package, but the Manifest declaration can still have the {@link org.openecomp.sdc.tosca.csar.CSARConstants#ARTIFACTS_FOLDER} reference in
177      * it. If so, that reference is removed.
178      *
179      * @param nonManoOriginalFilePath The original non mano file path
180      * @return The non mano fixed path to ONAP package structure.
181      */
182     private Path fixNonManoPath(final Path nonManoOriginalFilePath) {
183         final Path rootArtifactsPath = Paths.get("/", ARTIFACTS_FOLDER);
184         if (nonManoOriginalFilePath.startsWith(rootArtifactsPath)) {
185             return rootArtifactsPath.relativize(nonManoOriginalFilePath);
186         }
187         final Path relativeArtifactsPath = Paths.get(ARTIFACTS_FOLDER);
188         if (nonManoOriginalFilePath.startsWith(relativeArtifactsPath)) {
189             return relativeArtifactsPath.relativize(nonManoOriginalFilePath);
190         }
191         return nonManoOriginalFilePath;
192     }
193
194     @Override
195     public void updateMainDescriptorPaths(final ToscaServiceModel toscaServiceModel, final Map<String, Path> fromToMovedArtifactMap) {
196         final ServiceTemplate entryDefinition = toscaServiceModel.getServiceTemplates().get(toscaServiceModel.getEntryDefinitionServiceTemplate());
197         final YamlUtil yamlUtil = new YamlUtil();
198         final String[] entryDefinitionYaml = {yamlUtil.objectToYaml(entryDefinition)};
199         fromToMovedArtifactMap.forEach((fromPath, toPath) -> entryDefinitionYaml[0] = entryDefinitionYaml[0].replaceAll(fromPath, toPath.toString()));
200         toscaServiceModel.addServiceTemplate(toscaServiceModel.getEntryDefinitionServiceTemplate(),
201             yamlUtil.yamlToObject(entryDefinitionYaml[0], ServiceTemplate.class));
202     }
203
204     private boolean hasMetaMandatoryEntries(final ToscaMetadata toscaMetadata) {
205         final Map<String, String> metaDataEntries = toscaMetadata.getMetaEntries();
206         return metaDataEntries.containsKey(ENTRY_DEFINITIONS.getName()) && metaDataEntries.containsKey(ETSI_ENTRY_MANIFEST.getName())
207             && metaDataEntries.containsKey(ETSI_ENTRY_CHANGE_LOG.getName());
208     }
209
210     @Override
211     public Semver getHighestCompatibleSpecificationVersion(final FileContentHandler handler) {
212         try {
213             Map<String, String> metadata = getManifest(handler).getMetadata();
214             if (metadata.containsKey(COMPATIBLE_SPECIFICATION_VERSIONS.getToken())) {
215                 return Arrays.stream(metadata.get(COMPATIBLE_SPECIFICATION_VERSIONS.getToken()).split(",")).map(String::trim).map(Semver::new)
216                     .max(Comparator.naturalOrder()).orElse(new Semver(ETSI_VERSION_2_6_1));
217             }
218         } catch (Exception ex) {
219             LOGGER.error("An error occurred while getting highest compatible version from manifest file", ex);
220         }
221         return new Semver(ETSI_VERSION_2_6_1);
222     }
223
224     @Override
225     public boolean hasCnfEnhancements(final FileContentHandler fileContentHandler) throws IOException {
226         final Manifest manifest = loadManifest(fileContentHandler);
227         return manifest.getNonManoSources().entrySet().stream()
228             .filter(manifestNonManoSourceEntry -> NonManoArtifactType.ONAP_CNF_HELM.getType().equalsIgnoreCase(manifestNonManoSourceEntry.getKey()))
229             .findFirst().isPresent();
230     }
231
232     private Manifest loadManifest(final FileContentHandler handler) throws IOException {
233         final Manifest manifest;
234         try {
235             manifest = getManifest(handler);
236         } catch (final IOException ex) {
237             if (LOGGER.isErrorEnabled()) {
238                 LOGGER.error("An error occurred while getting the manifest file", ex);
239             }
240             throw ex;
241         }
242         return manifest;
243     }
244
245     private boolean isMetaFilePresent(Map<String, byte[]> handler) {
246         return handler.containsKey(TOSCA_META_PATH_FILE_NAME) || handler.containsKey(TOSCA_META_ORIG_PATH_FILE_NAME);
247     }
248
249     public ResourceTypeEnum getResourceType(FileContentHandler handler) throws IOException {
250         ToscaMetadata metadata = getMetadata(handler);
251         Manifest manifest = getManifest(handler, getEntryManifestLocation(metadata));
252         return getResourceType(manifest);
253     }
254
255     public ResourceTypeEnum getResourceType(Manifest manifest) {
256         // Valid manifest should contain whether vnf or pnf related metadata data exclusively in SOL004 standard,
257
258         // validation of manifest done during package upload stage
259         if (manifest != null && !manifest.getMetadata().isEmpty() && MANIFEST_PNF_METADATA.stream()
260             .anyMatch(e -> manifest.getMetadata().containsKey(e))) {
261             return ResourceTypeEnum.PNF;
262         }
263         // VNF is default resource type
264         return ResourceTypeEnum.VF;
265     }
266
267     public Manifest getManifest(FileContentHandler handler) throws IOException {
268         ToscaMetadata metadata = getMetadata(handler);
269         return getManifest(handler, getEntryManifestLocation(metadata));
270     }
271
272     private String getEntryManifestLocation(final ToscaMetadata metadata) {
273         return metadata.getMetaEntries().containsKey(ETSI_ENTRY_MANIFEST.getName()) ?
274             metadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName()):
275             metadata.getMetaEntries().get(ENTRY_MANIFEST.getName());
276     }
277
278     private Manifest getManifest(FileContentHandler handler, String manifestLocation) throws IOException {
279         try (InputStream manifestInputStream = getManifestInputStream(handler, manifestLocation)) {
280             Manifest onboardingManifest = new SOL004ManifestOnboarding();
281             onboardingManifest.parse(manifestInputStream);
282             return onboardingManifest;
283         }
284     }
285
286     public Path getOriginalManifestPath(final FileContentHandler handler) throws IOException {
287         final ToscaMetadata metadata = getOriginalMetadata(handler);
288         final String originalMetadataPath = getEntryManifestLocation(metadata);
289         final Path path = Paths.get(originalMetadataPath);
290         return path.getParent() == null ? Paths.get("") : path.getParent();
291     }
292
293     private ToscaMetadata getMetadata(FileContentHandler handler) throws IOException {
294         ToscaMetadata metadata;
295         if (handler.containsFile(TOSCA_META_PATH_FILE_NAME)) {
296             metadata = OnboardingToscaMetadata.parseToscaMetadataFile(handler.getFileContentAsStream(TOSCA_META_PATH_FILE_NAME));
297         } else if (handler.containsFile(TOSCA_META_ORIG_PATH_FILE_NAME)) {
298             metadata = OnboardingToscaMetadata.parseToscaMetadataFile(handler.getFileContentAsStream(TOSCA_META_ORIG_PATH_FILE_NAME));
299         } else {
300             throw new IOException("TOSCA.meta file not found!");
301         }
302         return metadata;
303     }
304
305     private ToscaMetadata getOriginalMetadata(final FileContentHandler handler) throws IOException {
306         if (handler.containsFile(TOSCA_META_ORIG_PATH_FILE_NAME)) {
307             return OnboardingToscaMetadata.parseToscaMetadataFile(handler.getFileContentAsStream(TOSCA_META_ORIG_PATH_FILE_NAME));
308         } else {
309             throw new IOException(String.format("%s file not found", TOSCA_META_ORIG_PATH_FILE_NAME));
310         }
311     }
312
313     private InputStream getManifestInputStream(FileContentHandler handler, String manifestLocation) throws IOException {
314         InputStream io;
315         if (manifestLocation == null || !handler.containsFile(manifestLocation)) {
316             io = handler.getFileContentAsStream(MAIN_SERVICE_TEMPLATE_MF_FILE_NAME);
317         } else {
318             io = handler.getFileContentAsStream(manifestLocation);
319         }
320         if (io == null) {
321             throw new IOException("Manifest file not found!");
322         }
323         return io;
324     }
325
326     public NonManoConfiguration getConfiguration() {
327         return nonManoConfiguration;
328     }
329
330 }