d99848ddb8355ef0e62ad4a5aff1d89883f08e08
[sdc.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2019 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  *  * Modifications copyright (c) 2020 Nokia
20  * ================================================================================
21  */
22 package org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation;
23
24 import static org.openecomp.sdc.be.config.NonManoArtifactType.ONAP_CNF_HELM;
25 import static org.openecomp.sdc.be.config.NonManoArtifactType.ONAP_PM_DICTIONARY;
26 import static org.openecomp.sdc.be.config.NonManoArtifactType.ONAP_SW_INFORMATION;
27 import static org.openecomp.sdc.be.config.NonManoArtifactType.ONAP_VES_EVENTS;
28 import static org.openecomp.sdc.tosca.csar.CSARConstants.CSAR_VERSION_1_0;
29 import static org.openecomp.sdc.tosca.csar.CSARConstants.CSAR_VERSION_1_1;
30 import static org.openecomp.sdc.tosca.csar.CSARConstants.MANIFEST_METADATA_LIMIT;
31 import static org.openecomp.sdc.tosca.csar.CSARConstants.MANIFEST_PNF_METADATA;
32 import static org.openecomp.sdc.tosca.csar.CSARConstants.MANIFEST_VNF_METADATA;
33 import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_MANIFEST_FILE_EXT;
34 import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_TYPE_PNF;
35 import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_TYPE_VNF;
36 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.CREATED_BY_ENTRY;
37 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.CSAR_VERSION_ENTRY;
38 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.ENTRY_DEFINITIONS;
39 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.ETSI_ENTRY_CERTIFICATE;
40 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.ETSI_ENTRY_MANIFEST;
41 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.TOSCA_META_FILE_VERSION_ENTRY;
42 import static org.openecomp.sdc.tosca.csar.ToscaMetadataFileInfo.TOSCA_META_FILE_VERSION_1_0;
43 import static org.openecomp.sdc.tosca.csar.ToscaMetadataFileInfo.TOSCA_META_PATH_FILE_NAME;
44
45 import com.google.common.collect.ImmutableSet;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.util.ArrayList;
49 import java.util.Collections;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Optional;
55 import java.util.Set;
56 import java.util.concurrent.CopyOnWriteArrayList;
57 import java.util.stream.Collectors;
58 import java.util.stream.Stream;
59 import org.apache.commons.collections.CollectionUtils;
60 import org.apache.commons.io.FilenameUtils;
61 import org.openecomp.core.impl.ToscaDefinitionImportHandler;
62 import org.openecomp.core.utilities.file.FileContentHandler;
63 import org.openecomp.sdc.be.config.NonManoArtifactType;
64 import org.openecomp.sdc.be.csar.pnf.PnfSoftwareInformation;
65 import org.openecomp.sdc.be.csar.pnf.SoftwareInformationArtifactYamlParser;
66 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
67 import org.openecomp.sdc.common.errors.Messages;
68 import org.openecomp.sdc.common.utils.SdcCommon;
69 import org.openecomp.sdc.datatypes.error.ErrorLevel;
70 import org.openecomp.sdc.datatypes.error.ErrorMessage;
71 import org.openecomp.sdc.logging.api.Logger;
72 import org.openecomp.sdc.logging.api.LoggerFactory;
73 import org.openecomp.sdc.tosca.csar.Manifest;
74 import org.openecomp.sdc.tosca.csar.OnboardingToscaMetadata;
75 import org.openecomp.sdc.tosca.csar.SOL004ManifestOnboarding;
76 import org.openecomp.sdc.tosca.csar.ToscaMetaEntry;
77 import org.openecomp.sdc.tosca.csar.ToscaMetadata;
78 import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.OnboardingPackageContentHandler;
79 import org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation.exception.MissingCertificateException;
80 import org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation.utils.FileExtractor;
81 import org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation.utils.InternalFilesFilter;
82 import org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation.utils.ValidatorUtils;
83 import org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.exceptions.InvalidManifestMetadataException;
84 import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager;
85 import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException;
86 import org.yaml.snakeyaml.Yaml;
87
88 /**
89  * Validates the contents of the package to ensure it complies with the "CSAR with TOSCA-Metadata directory" structure as defined in ETSI GS NFV-SOL
90  * 004 v2.6.1.
91  */
92 class SOL004MetaDirectoryValidator implements Validator {
93
94     private static final Logger LOGGER = LoggerFactory.getLogger(SOL004MetaDirectoryValidator.class);
95     private static final String MANIFEST_SOURCE = "Source";
96     private static final String MANIFEST_NON_MANO_SOURCE = "Non-MANO Source";
97     protected final ValidatorUtils validatorUtils = new ValidatorUtils();
98     private final List<ErrorMessage> errorsByFile = new CopyOnWriteArrayList<>();
99     private final SecurityManager securityManager;
100     private final InternalFilesFilter internalFilesFilter = new InternalFilesFilter();
101     private OnboardingPackageContentHandler contentHandler;
102     private Set<String> folderList;
103     private ToscaMetadata toscaMetadata;
104
105     public SOL004MetaDirectoryValidator() {
106         securityManager = SecurityManager.getInstance();
107     }
108
109     //for tests purpose
110     SOL004MetaDirectoryValidator(final SecurityManager securityManager) {
111         this.securityManager = securityManager;
112     }
113
114     @Override
115     public Map<String, List<ErrorMessage>> validateContent(final FileContentHandler fileContentHandler) {
116         this.contentHandler = (OnboardingPackageContentHandler) fileContentHandler;
117         this.folderList = contentHandler.getFolderList();
118         parseToscaMetadata();
119         verifyMetadataFile();
120         if (packageHasCertificate()) {
121             verifySignedFiles();
122         }
123         validatePmDictionaryContentsAgainstSchema();
124         return Collections.unmodifiableMap(getAnyValidationErrors());
125     }
126
127     private boolean packageHasCertificate() {
128         final String certificatePath = getCertificatePath().orElse(null);
129         return contentHandler.containsFile(certificatePath);
130     }
131
132     private Optional<String> getCertificatePath() {
133         return toscaMetadata.getEntry(ETSI_ENTRY_CERTIFICATE);
134     }
135
136     /**
137      * Parses the {@link org.openecomp.sdc.tosca.csar.ToscaMetadataFileInfo#TOSCA_META_PATH_FILE_NAME} file
138      */
139     private void parseToscaMetadata() {
140         try {
141             toscaMetadata = OnboardingToscaMetadata.parseToscaMetadataFile(contentHandler.getFileContentAsStream(TOSCA_META_PATH_FILE_NAME));
142         } catch (final IOException e) {
143             reportError(ErrorLevel.ERROR, Messages.METADATA_PARSER_INTERNAL.getErrorMessage());
144             LOGGER.error(Messages.METADATA_PARSER_INTERNAL.getErrorMessage(), e.getMessage(), e);
145         }
146     }
147
148     private void verifyMetadataFile() {
149         if (toscaMetadata.isValid() && hasETSIMetadata()) {
150             verifyManifestNameAndExtension();
151             handleMetadataEntries();
152         } else {
153             errorsByFile.addAll(toscaMetadata.getErrors());
154         }
155     }
156
157     private void verifySignedFiles() {
158         final Map<String, String> signedFileMap = contentHandler.getFileAndSignaturePathMap(SecurityManager.ALLOWED_SIGNATURE_EXTENSIONS);
159         final String packageCertificatePath = getCertificatePath().orElse(null);
160         final byte[] packageCert = contentHandler.getFileContent(packageCertificatePath);
161         if (packageCert == null) {
162             throw new MissingCertificateException("Expected package certificate");
163         }
164         signedFileMap.entrySet().stream().filter(entry -> entry.getValue() != null).forEach(entry -> {
165             final String filePath = entry.getKey();
166             final String fileSignaturePath = entry.getValue();
167             final byte[] fileBytes = contentHandler.getFileContent(filePath);
168             final byte[] fileSignatureBytes = contentHandler.getFileContent(fileSignaturePath);
169             try {
170                 if (!securityManager.verifySignedData(fileSignatureBytes, packageCert, fileBytes)) {
171                     reportError(ErrorLevel.ERROR, Messages.ARTIFACT_INVALID_SIGNATURE.formatMessage(fileSignaturePath, filePath));
172                 }
173             } catch (final SecurityManagerException e) {
174                 final String errorMessage = Messages.ARTIFACT_SIGNATURE_VALIDATION_ERROR
175                     .formatMessage(fileSignaturePath, filePath, packageCertificatePath, e.getMessage());
176                 reportError(ErrorLevel.ERROR, errorMessage);
177                 LOGGER.error(errorMessage, e);
178             }
179         });
180     }
181
182     private void verifyManifestNameAndExtension() {
183         final Map<String, String> entries = toscaMetadata.getMetaEntries();
184         final String manifestFileName = getFileName(entries.get(ETSI_ENTRY_MANIFEST.getName()));
185         final String manifestExtension = getFileExtension(entries.get(ETSI_ENTRY_MANIFEST.getName()));
186         final String mainDefinitionFileName = getFileName(entries.get(ENTRY_DEFINITIONS.getName()));
187         if (!(TOSCA_MANIFEST_FILE_EXT).equals(manifestExtension)) {
188             reportError(ErrorLevel.ERROR, Messages.MANIFEST_INVALID_EXT.getErrorMessage());
189         }
190         if (!mainDefinitionFileName.equals(manifestFileName)) {
191             reportError(ErrorLevel.ERROR, String.format(Messages.MANIFEST_INVALID_NAME.getErrorMessage(), manifestFileName, mainDefinitionFileName));
192         }
193     }
194
195     private String getFileExtension(final String filePath) {
196         return FilenameUtils.getExtension(filePath);
197     }
198
199     private String getFileName(final String filePath) {
200         return FilenameUtils.getBaseName(filePath);
201     }
202
203     private boolean hasETSIMetadata() {
204         final Map<String, String> entries = toscaMetadata.getMetaEntries();
205         return hasEntry(entries, TOSCA_META_FILE_VERSION_ENTRY.getName()) && hasEntry(entries, CSAR_VERSION_ENTRY.getName()) && hasEntry(entries,
206             CREATED_BY_ENTRY.getName());
207     }
208
209     private boolean hasEntry(final Map<String, String> entries, final String mandatoryEntry) {
210         if (!entries.containsKey(mandatoryEntry)) {
211             reportError(ErrorLevel.ERROR, String.format(Messages.METADATA_MISSING_ENTRY.getErrorMessage(), mandatoryEntry));
212             return false;
213         }
214         return true;
215     }
216
217     private void handleMetadataEntries() {
218         toscaMetadata.getMetaEntries().entrySet().parallelStream().forEach(this::handleEntry);
219     }
220
221     private void handleEntry(final Map.Entry<String, String> entry) {
222         final String key = entry.getKey();
223         final ToscaMetaEntry toscaMetaEntry = ToscaMetaEntry.parse(entry.getKey()).orElse(null);
224         // allows any other unknown entry
225         if (toscaMetaEntry == null) {
226             return;
227         }
228         final String value = entry.getValue();
229         switch (toscaMetaEntry) {
230             case TOSCA_META_FILE_VERSION_ENTRY:
231             case CSAR_VERSION_ENTRY:
232             case CREATED_BY_ENTRY:
233                 verifyMetadataEntryVersions(key, value);
234                 break;
235             case ENTRY_DEFINITIONS:
236                 validateDefinitionFile(value);
237                 break;
238             case ETSI_ENTRY_MANIFEST:
239                 validateManifestFile(value);
240                 break;
241             case ETSI_ENTRY_CHANGE_LOG:
242                 validateChangeLog(value);
243                 break;
244             case ETSI_ENTRY_TESTS:
245             case ETSI_ENTRY_LICENSES:
246                 validateOtherEntries(entry);
247                 break;
248             case ETSI_ENTRY_CERTIFICATE:
249                 validateCertificate(value);
250                 break;
251             default:
252                 handleOtherEntry(entry);
253                 break;
254         }
255     }
256
257     private void validateOtherEntries(final Map.Entry<String, String> entry) {
258         final String manifestFile = toscaMetadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName());
259         if (verifyFileExists(contentHandler.getFileList(), manifestFile)) {
260             final Manifest onboardingManifest = new SOL004ManifestOnboarding();
261             onboardingManifest.parse(contentHandler.getFileContentAsStream(manifestFile));
262             final Optional<ResourceTypeEnum> resourceType = onboardingManifest.getType();
263             if (resourceType.isPresent() && resourceType.get() == ResourceTypeEnum.VF) {
264                 final String value = (String) entry.getValue();
265                 validateOtherEntries(value);
266             } else {
267                 final String key = (String) entry.getKey();
268                 reportError(ErrorLevel.ERROR, String.format(Messages.MANIFEST_INVALID_PNF_METADATA.getErrorMessage(), key));
269             }
270         }
271     }
272
273     private void verifyMetadataEntryVersions(final String key, final String version) {
274         if (!(isValidTOSCAVersion(key, version) || isValidCSARVersion(key, version) || CREATED_BY_ENTRY.getName().equals(key))) {
275             errorsByFile.add(new ErrorMessage(ErrorLevel.ERROR, String.format(Messages.METADATA_INVALID_VERSION.getErrorMessage(), key, version)));
276             LOGGER.error("{}: key {} - value {} ", Messages.METADATA_INVALID_VERSION.getErrorMessage(), key, version);
277         }
278     }
279
280     private boolean isValidTOSCAVersion(final String key, final String version) {
281         return TOSCA_META_FILE_VERSION_ENTRY.getName().equals(key) && TOSCA_META_FILE_VERSION_1_0.equals(version);
282     }
283
284     private boolean isValidCSARVersion(final String value, final String version) {
285         return CSAR_VERSION_ENTRY.getName().equals(value) && (CSAR_VERSION_1_1.equals(version) || CSAR_VERSION_1_0.equals(version));
286     }
287
288     protected void validateDefinitionFile(final String filePath) {
289         final Set<String> existingFiles = contentHandler.getFileList();
290         if (verifyFileExists(existingFiles, filePath)) {
291             final ToscaDefinitionImportHandler toscaDefinitionImportHandler = new ToscaDefinitionImportHandler(contentHandler.getFiles(), filePath);
292             final List<ErrorMessage> validationErrorList = toscaDefinitionImportHandler.getErrors();
293             if (CollectionUtils.isNotEmpty(validationErrorList)) {
294                 errorsByFile.addAll(validationErrorList);
295             }
296         } else {
297             reportError(ErrorLevel.ERROR, String.format(Messages.MISSING_DEFINITION_FILE.getErrorMessage(), filePath));
298         }
299     }
300
301     private boolean verifyFileExists(final Set<String> existingFiles, final String filePath) {
302         return existingFiles.contains(filePath);
303     }
304
305     private void validateManifestFile(final String filePath) {
306         final Set<String> existingFiles = contentHandler.getFileList();
307         if (verifyFileExists(existingFiles, filePath)) {
308             final Manifest onboardingManifest = new SOL004ManifestOnboarding();
309             onboardingManifest.parse(contentHandler.getFileContentAsStream(filePath));
310             if (onboardingManifest.isValid()) {
311                 try {
312                     verifyManifestMetadata(onboardingManifest.getMetadata());
313                 } catch (final InvalidManifestMetadataException e) {
314                     reportError(ErrorLevel.ERROR, e.getMessage());
315                     LOGGER.error(e.getMessage(), e);
316                 }
317                 verifyManifestSources(onboardingManifest);
318             } else {
319                 final List<String> manifestErrors = onboardingManifest.getErrors();
320                 manifestErrors.forEach(error -> reportError(ErrorLevel.ERROR, error));
321             }
322         } else {
323             reportError(ErrorLevel.ERROR, String.format(Messages.MANIFEST_NOT_FOUND.getErrorMessage(), filePath));
324         }
325     }
326
327     private void verifyManifestMetadata(final Map<String, String> metadata) {
328         if (!validMetaLimit(metadata)) {
329             reportError(ErrorLevel.ERROR, String.format(Messages.MANIFEST_METADATA_DOES_NOT_MATCH_LIMIT.getErrorMessage(), MANIFEST_METADATA_LIMIT));
330         }
331         handleMetadataEntries(metadata);
332     }
333
334     protected boolean isPnfMetadata(final Map<String, String> metadata) {
335         final String firstMetadataDefinition = metadata.keySet().iterator().next();
336         final String expectedMetadataType = firstMetadataDefinition.contains(TOSCA_TYPE_PNF) ? TOSCA_TYPE_PNF : TOSCA_TYPE_VNF;
337         if (metadata.keySet().stream().anyMatch((final String metadataEntry) -> !metadataEntry.contains(expectedMetadataType))) {
338             throw new InvalidManifestMetadataException(Messages.MANIFEST_METADATA_INVALID_ENTRY.getErrorMessage());
339         }
340         return TOSCA_TYPE_PNF.equals(expectedMetadataType);
341     }
342
343     private void handleMetadataEntries(final Map<String, String> metadata) {
344         getManifestMetadata(metadata).stream().filter(requiredEntry -> !metadata.containsKey(requiredEntry)).forEach(
345             requiredEntry -> reportError(ErrorLevel.ERROR, String.format(Messages.MANIFEST_METADATA_MISSING_ENTRY.getErrorMessage(), requiredEntry)));
346     }
347
348     /**
349      * Checks if all manifest sources exists within the package and if all package files are being referred.
350      *
351      * @param onboardingManifest The manifest
352      */
353     private void verifyManifestSources(final Manifest onboardingManifest) {
354         final Set<String> packageFiles = contentHandler.getFileList();
355         final List<String> sources = internalFilesFilter.filter(onboardingManifest.getSources());
356         verifyFilesExist(packageFiles, sources, MANIFEST_SOURCE);
357         final Map<String, List<String>> nonManoArtifacts = onboardingManifest.getNonManoSources();
358         final List<String> nonManoValidFilePaths = new ArrayList<>();
359         nonManoArtifacts.forEach((nonManoType, files) -> {
360             final List<String> internalNonManoFileList = internalFilesFilter.filter(files);
361             nonManoValidFilePaths.addAll(internalNonManoFileList);
362             final NonManoArtifactType nonManoArtifactType = NonManoArtifactType.parse(nonManoType).orElse(null);
363             if (nonManoArtifactType == ONAP_PM_DICTIONARY || nonManoArtifactType == ONAP_VES_EVENTS) {
364                 internalNonManoFileList.forEach(this::validateYaml);
365             } else if (nonManoArtifactType == ONAP_SW_INFORMATION) {
366                 validateSoftwareInformationNonManoArtifact(files);
367             } else if (nonManoArtifactType == ONAP_CNF_HELM) {
368                 validateOnapCnfHelmNonManoEntry(files);
369             }
370         });
371         verifyFilesExist(packageFiles, nonManoValidFilePaths, MANIFEST_NON_MANO_SOURCE);
372         final Set<String> allReferredFiles = new HashSet<>();
373         allReferredFiles.addAll(sources);
374         allReferredFiles.addAll(nonManoValidFilePaths);
375         verifyFilesBeingReferred(allReferredFiles, packageFiles);
376     }
377
378     private void validateSoftwareInformationNonManoArtifact(final List<String> files) {
379         if (CollectionUtils.isEmpty(files)) {
380             reportError(ErrorLevel.ERROR, Messages.EMPTY_SW_INFORMATION_NON_MANO_ERROR.getErrorMessage());
381             return;
382         }
383         if (files.size() != 1) {
384             final String formattedFileList = files.stream().map(filePath -> String.format("'%s'", filePath)).collect(Collectors.joining(", "));
385             reportError(ErrorLevel.ERROR, Messages.UNIQUE_SW_INFORMATION_NON_MANO_ERROR.formatMessage(formattedFileList));
386             return;
387         }
388         final String swInformationFilePath = files.get(0);
389         final byte[] swInformationYaml = contentHandler.getFileContent(swInformationFilePath);
390         final Optional<PnfSoftwareInformation> parsedYaml = SoftwareInformationArtifactYamlParser.parse(swInformationYaml);
391         if (!parsedYaml.isPresent()) {
392             reportError(ErrorLevel.ERROR, Messages.INVALID_SW_INFORMATION_NON_MANO_ERROR.formatMessage(swInformationFilePath));
393         } else {
394             final PnfSoftwareInformation pnfSoftwareInformation = parsedYaml.get();
395             if (!pnfSoftwareInformation.isValid()) {
396                 reportError(ErrorLevel.ERROR, Messages.INCORRECT_SW_INFORMATION_NON_MANO_ERROR.formatMessage(swInformationFilePath));
397             }
398         }
399     }
400
401     /**
402      * Validates if a YAML file has the correct extension, is not empty and the content is a valid YAML. Reports each error found.
403      *
404      * @param filePath the file path inside the package
405      */
406     private void validateYaml(final String filePath) {
407         if (!contentHandler.containsFile(filePath)) {
408             return;
409         }
410         final String fileExtension = getFileExtension(filePath);
411         if (!"yaml".equalsIgnoreCase(fileExtension) && !"yml".equalsIgnoreCase(fileExtension)) {
412             reportError(ErrorLevel.ERROR, Messages.INVALID_YAML_EXTENSION.formatMessage(filePath));
413             return;
414         }
415         try (final InputStream fileContent = contentHandler.getFileContentAsStream(filePath)) {
416             if (fileContent == null) {
417                 reportError(ErrorLevel.ERROR, Messages.EMPTY_YAML_FILE_1.formatMessage(filePath));
418                 return;
419             }
420             new Yaml().loadAll(fileContent).iterator().next();
421         } catch (final IOException e) {
422             final String errorMsg = Messages.FILE_LOAD_CONTENT_ERROR.formatMessage(filePath);
423             reportError(ErrorLevel.ERROR, errorMsg);
424             LOGGER.debug(errorMsg, e);
425         } catch (final Exception e) {
426             final String message = Messages.INVALID_YAML_FORMAT_1.formatMessage(filePath, e.getMessage());
427             LOGGER.debug(message, e);
428             reportError(ErrorLevel.ERROR, message);
429         }
430     }
431
432     /**
433      * Checks if all package files are referred in manifest. Reports missing references.
434      *
435      * @param referredFileSet the list of referred files path
436      * @param packageFileSet  the list of package file path
437      */
438     private void verifyFilesBeingReferred(final Set<String> referredFileSet, final Set<String> packageFileSet) {
439         packageFileSet.forEach(filePath -> {
440             if (!isManifestFile(filePath) && !referredFileSet.contains(filePath)) {
441                 reportError(ErrorLevel.ERROR, String.format(Messages.MISSING_MANIFEST_REFERENCE.getErrorMessage(), filePath));
442             }
443         });
444     }
445
446     private boolean isManifestFile(final String filePath) {
447         return filePath.equals(toscaMetadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName()));
448     }
449
450     private void validateOtherEntries(final String folderPath) {
451         if (!verifyFoldersExist(folderList, folderPath)) {
452             reportError(ErrorLevel.ERROR, String.format(Messages.METADATA_MISSING_OPTIONAL_FOLDERS.getErrorMessage(), folderPath));
453         }
454     }
455
456     private void validateCertificate(final String file) {
457         final Set<String> packageFiles = contentHandler.getFileList();
458         if (!verifyFileExist(packageFiles, file)) {
459             reportError(ErrorLevel.ERROR, String.format(Messages.MISSING_METADATA_FILES.getErrorMessage(), file, file));
460         }
461     }
462
463     private boolean verifyFoldersExist(final Set<String> folderList, final String folderPath) {
464         return folderList.contains(folderPath + "/");
465     }
466
467     private void verifyFilesExist(final Set<String> existingFiles, final List<String> sources, final String type) {
468         sources.forEach(file -> {
469             if (!existingFiles.contains(file)) {
470                 reportError(ErrorLevel.ERROR, String.format(Messages.MISSING_MANIFEST_SOURCE.getErrorMessage(), type, file));
471             }
472         });
473     }
474
475     private boolean verifyFileExist(final Set<String> existingFiles, final String file) {
476         return existingFiles.contains(file);
477     }
478
479     private void validateChangeLog(final String filePath) {
480         if (!verifyFileExists(contentHandler.getFileList(), filePath)) {
481             reportError(ErrorLevel.ERROR, String.format(Messages.MISSING_METADATA_FILES.getErrorMessage(), filePath));
482         }
483     }
484
485     protected void reportError(final ErrorLevel errorLevel, final String errorMessage) {
486         errorsByFile.add(new ErrorMessage(errorLevel, errorMessage));
487     }
488
489     protected boolean validMetaLimit(Map<String, String> metadata) {
490         return metadata.size() == MANIFEST_METADATA_LIMIT;
491     }
492
493     protected ImmutableSet<String> getManifestMetadata(final Map<String, String> metadata) {
494         return isPnfMetadata(metadata) ? MANIFEST_PNF_METADATA : MANIFEST_VNF_METADATA;
495     }
496
497     protected void handleOtherEntry(final Map.Entry<String, String> entry) {
498         reportError(ErrorLevel.ERROR, Messages.METADATA_UNSUPPORTED_ENTRY.formatMessage(entry.getKey()));
499         LOGGER.warn(Messages.METADATA_UNSUPPORTED_ENTRY.getErrorMessage(), entry.getKey());
500     }
501
502     private Map<String, List<ErrorMessage>> getAnyValidationErrors() {
503         if (errorsByFile.isEmpty()) {
504             return Collections.emptyMap();
505         }
506         final Map<String, List<ErrorMessage>> errors = new HashMap<>();
507         errors.put(SdcCommon.UPLOAD_FILE, errorsByFile);
508         return errors;
509     }
510
511     private void validatePmDictionaryContentsAgainstSchema() {
512         final Stream<byte[]> pmDictionaryFiles = new FileExtractor(getEtsiEntryManifestPath(), contentHandler).findFiles(ONAP_PM_DICTIONARY);
513         new PMDictionaryValidator().validate(pmDictionaryFiles, (String message) -> reportError(ErrorLevel.ERROR, message));
514     }
515
516     private String getEtsiEntryManifestPath() {
517         return toscaMetadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName());
518     }
519
520     /**
521      * Validates if onap_cnf_helm non_mano type points to a file
522      *
523      * @param files
524      */
525     private void validateOnapCnfHelmNonManoEntry(final List<String> files) {
526         if (CollectionUtils.isEmpty(files)) {
527             reportError(ErrorLevel.ERROR, Messages.EMPTY_ONAP_CNF_HELM_NON_MANO_ERROR.getErrorMessage());
528             return;
529         }
530         if (files.size() != 1) {
531             final String formattedFileList = files.stream().map(filePath -> String.format("'%s'", filePath)).collect(Collectors.joining(", "));
532             reportError(ErrorLevel.ERROR, Messages.UNIQUE_ONAP_CNF_HELM_NON_MANO_ERROR.formatMessage(formattedFileList));
533         }
534     }
535 }