2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2023 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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
20 package org.openecomp.sdc.be.tosca;
22 import static org.openecomp.sdc.be.dao.api.ActionStatus.ARTIFACT_PAYLOAD_NOT_FOUND_DURING_CSAR_CREATION;
23 import static org.openecomp.sdc.be.dao.api.ActionStatus.ERROR_DURING_CSAR_CREATION;
24 import static org.openecomp.sdc.be.tosca.ComponentCache.MergeStrategy.overwriteIfSameVersions;
25 import static org.openecomp.sdc.be.tosca.FJToVavrHelper.Try0.fromEither;
26 import static org.openecomp.sdc.be.tosca.FJToVavrHelper.Try0.javaListToVavrList;
27 import static org.openecomp.sdc.common.api.Constants.ADDITIONAL_TYPE_DEFINITIONS;
29 import com.google.common.primitives.Bytes;
31 import fj.data.Either;
32 import io.vavr.Tuple2;
33 import io.vavr.control.Try;
34 import java.io.BufferedOutputStream;
35 import java.io.ByteArrayInputStream;
37 import java.io.IOException;
38 import java.nio.charset.StandardCharsets;
39 import java.nio.file.Path;
40 import java.nio.file.Paths;
41 import java.text.SimpleDateFormat;
42 import java.util.ArrayList;
43 import java.util.Date;
44 import java.util.EnumMap;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.List;
49 import java.util.Map.Entry;
50 import java.util.Objects;
51 import java.util.Optional;
53 import java.util.TimeZone;
54 import java.util.function.Function;
55 import java.util.function.Predicate;
56 import java.util.function.Supplier;
57 import java.util.regex.Matcher;
58 import java.util.regex.Pattern;
59 import java.util.stream.Collectors;
60 import java.util.stream.Stream;
61 import java.util.zip.ZipEntry;
62 import java.util.zip.ZipInputStream;
63 import java.util.zip.ZipOutputStream;
64 import org.apache.commons.collections.CollectionUtils;
65 import org.apache.commons.collections.MapUtils;
66 import org.apache.commons.io.output.ByteArrayOutputStream;
67 import org.apache.commons.lang.StringUtils;
68 import org.apache.commons.lang3.tuple.ImmutableTriple;
69 import org.apache.commons.lang3.tuple.Triple;
70 import org.apache.commons.text.WordUtils;
71 import org.onap.sdc.tosca.services.YamlUtil;
72 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
73 import org.openecomp.sdc.be.config.CategoryBaseTypeConfig;
74 import org.openecomp.sdc.be.config.ConfigurationManager;
75 import org.openecomp.sdc.be.dao.api.ActionStatus;
76 import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
77 import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
78 import org.openecomp.sdc.be.dao.cassandra.SdcSchemaFilesCassandraDao;
79 import org.openecomp.sdc.be.data.model.ToscaImportByModel;
80 import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
81 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
82 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
83 import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
84 import org.openecomp.sdc.be.impl.ComponentsUtils;
85 import org.openecomp.sdc.be.model.ArtifactDefinition;
86 import org.openecomp.sdc.be.model.Component;
87 import org.openecomp.sdc.be.model.ComponentInstance;
88 import org.openecomp.sdc.be.model.InterfaceDefinition;
89 import org.openecomp.sdc.be.model.LifecycleStateEnum;
90 import org.openecomp.sdc.be.model.Resource;
91 import org.openecomp.sdc.be.model.Service;
92 import org.openecomp.sdc.be.model.category.CategoryDefinition;
93 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
94 import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter;
95 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
96 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
97 import org.openecomp.sdc.be.model.operations.impl.ModelOperation;
98 import org.openecomp.sdc.be.plugins.CsarEntryGenerator;
99 import org.openecomp.sdc.be.resources.data.DAOArtifactData;
100 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
101 import org.openecomp.sdc.be.utils.TypeUtils;
102 import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
103 import org.openecomp.sdc.common.api.ArtifactTypeEnum;
104 import org.openecomp.sdc.common.impl.ExternalConfiguration;
105 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
106 import org.openecomp.sdc.common.util.GeneralUtility;
107 import org.openecomp.sdc.common.zip.ZipUtils;
108 import org.openecomp.sdc.exception.ResponseFormat;
109 import org.slf4j.Logger;
110 import org.slf4j.LoggerFactory;
111 import org.springframework.beans.factory.annotation.Autowired;
112 import org.yaml.snakeyaml.DumperOptions;
113 import org.yaml.snakeyaml.Yaml;
116 * Generates a Network Service CSAR based on a SERVICE component and wraps it in a SDC CSAR entry.
118 @org.springframework.stereotype.Component("commonCsarGenerator")
119 public class CommonCsarGenerator {
121 private static final Logger LOGGER = LoggerFactory.getLogger(CommonCsarGenerator.class);
122 public static final String ARTIFACTS_PATH = "Artifacts/";
123 private static final String RESOURCES_PATH = "Resources/";
124 private static final String PATH_DELIMITER = "/";
125 private static final String SERVICE_MANIFEST = "NS.mf";
126 private static final String ARTIFACT_NAME_UNIQUE_ID = "ArtifactName {}, unique ID {}";
127 private static final String TOSCA_META_PATH_FILE_NAME = "TOSCA-Metadata/TOSCA.meta";
128 private static final String TOSCA_META_VERSION = "1.0";
129 private static final String CSAR_VERSION = "1.1";
130 private static final String SDC_VERSION = ExternalConfiguration.getAppVersion();
131 public static final String NODES_YML = "nodes.yml";
132 private static final String CONFORMANCE_LEVEL = ConfigurationManager.getConfigurationManager().getConfiguration().getToscaConformanceLevel();
133 private final ToscaOperationFacade toscaOperationFacade;
134 private final ComponentsUtils componentsUtils;
135 private final ToscaExportHandler toscaExportUtils;
136 private final List<CsarEntryGenerator> generators;
137 private final ArtifactCassandraDao artifactCassandraDao;
138 private final String versionFirstThreeOctets;
139 private final SdcSchemaFilesCassandraDao sdcSchemaFilesCassandraDao;
140 private final ModelOperation modelOperation;
143 public CommonCsarGenerator(
144 final ToscaOperationFacade toscaOperationFacade,
145 final ComponentsUtils componentsUtils,
146 final ToscaExportHandler toscaExportUtils,
147 final List<CsarEntryGenerator> generators,
148 final ArtifactCassandraDao artifactCassandraDao,
149 final SdcSchemaFilesCassandraDao sdcSchemaFilesCassandraDao,
150 final ModelOperation modelOperation) {
151 this.toscaOperationFacade = toscaOperationFacade;
152 this.componentsUtils = componentsUtils;
153 this.toscaExportUtils = toscaExportUtils;
154 this.generators = generators;
155 this.artifactCassandraDao = artifactCassandraDao;
156 this.versionFirstThreeOctets = readVersionFirstThreeOctets();
157 this.sdcSchemaFilesCassandraDao = sdcSchemaFilesCassandraDao;
158 this.modelOperation = modelOperation;
161 private String readVersionFirstThreeOctets() {
162 if (StringUtils.isEmpty(SDC_VERSION)) {
165 // change regex to avoid DoS sonar issue
166 Matcher matcher = Pattern.compile("(?!\\.)(\\d{1,9}(\\.\\d{1,9}){1,9})(?![\\d\\.])").matcher(SDC_VERSION);
168 return matcher.group(0);
172 * Generates a Network Service CSAR based on a SERVICE component that has category configured in
173 * CategoriesToGenerateNsd enum and wraps it in a SDC CSAR entry.
175 * @param component the component to create the NS CSAR from
176 * @return an entry to be added in the Component CSAR by SDC
179 public Either<ZipOutputStream, ResponseFormat> generateCsarZip(Component component,
182 boolean isInCertificationRequest,
183 boolean isAsdPackage,
184 String definitionsPath,
185 boolean addDependencies,
186 boolean isSkipImports) throws IOException {
187 ArtifactDefinition artifactDef = component.getToscaArtifacts().get(ToscaExportHandler.ASSET_TOSCA_TEMPLATE);
188 Either<ToscaRepresentation, ResponseFormat> toscaRepresentation = fetchToscaRepresentation(component, getFromCS, artifactDef, isSkipImports);
190 // This should not be done but in order to keep the refactoring small enough we stop here.
191 // TODO: Refactor the rest of this function
193 List<Triple<String, String, Component>> dependencies;
194 if (toscaRepresentation.isLeft()) {
195 mainYaml = toscaRepresentation.left().value().getMainYaml();
196 dependencies = toscaRepresentation.left().value().getDependencies().getOrElse(new ArrayList<>());
198 return Either.right(toscaRepresentation.right().value());
200 final String fileName = artifactDef.getArtifactName();
201 final byte[] toscaBlock0Byte =
202 createToscaBlock0(TOSCA_META_VERSION, CSAR_VERSION, component.getCreatorFullName(), fileName, isAsdPackage, definitionsPath).getBytes();
203 zip.putNextEntry(new ZipEntry(TOSCA_META_PATH_FILE_NAME));
204 zip.write(toscaBlock0Byte);
205 zip.putNextEntry(new ZipEntry(definitionsPath + fileName));
207 LifecycleStateEnum lifecycleState = component.getLifecycleState();
208 addServiceMf(component, zip, lifecycleState, isInCertificationRequest, fileName, mainYaml, definitionsPath);
209 if (addDependencies) {
210 //US798487 - Abstraction of complex types
211 if (hasToWriteComponentSubstitutionType(component)) {
212 LOGGER.debug("Component {} is complex - generating abstract type for it..", component.getName());
213 dependencies.addAll(writeComponentInterface(component, zip, fileName, definitionsPath));
215 //UID <cassandraId,filename,component>
216 Either<ZipOutputStream, ResponseFormat> zipOutputStreamOrResponseFormat =
217 getZipOutputStreamResponseFormatEither(zip, dependencies, definitionsPath);
218 if (zipOutputStreamOrResponseFormat != null && zipOutputStreamOrResponseFormat.isRight()) {
219 return zipOutputStreamOrResponseFormat;
222 if (component.getModel() == null) {
223 //retrieve SDC.zip from Cassandra
224 Either<byte[], ResponseFormat> latestSchemaFiles = getLatestSchemaFilesFromCassandra();
225 if (latestSchemaFiles.isRight()) {
226 LOGGER.error("Error retrieving SDC Schema files from cassandra");
227 return Either.right(latestSchemaFiles.right().value());
229 final byte[] schemaFileZip = latestSchemaFiles.left().value();
230 final List<String> nodesFromPackage = findNonRootNodesFromPackage(dependencies);
231 //add files from retrieved SDC.zip to Definitions folder in CSAR
232 addSchemaFilesFromCassandra(zip, schemaFileZip, nodesFromPackage, definitionsPath);
234 //retrieve schema files by model from Cassandra
235 addSchemaFilesByModel(zip, component.getModel(), definitionsPath, addDependencies);
237 Either<CsarDefinition, ResponseFormat> collectedComponentCsarDefinition = collectComponentCsarDefinition(component);
238 if (collectedComponentCsarDefinition.isRight()) {
239 return Either.right(collectedComponentCsarDefinition.right().value());
241 if (generators != null) {
242 for (CsarEntryGenerator generator : generators) {
243 LOGGER.debug("Invoking CsarEntryGenerator: {}", generator.getClass().getName());
244 for (Map.Entry<String, byte[]> pluginGeneratedFile : generator.generateCsarEntries(component).entrySet()) {
245 zip.putNextEntry(new ZipEntry(pluginGeneratedFile.getKey()));
246 zip.write(pluginGeneratedFile.getValue());
250 return writeAllFilesToCsar(component, collectedComponentCsarDefinition.left().value(), zip, isInCertificationRequest);
253 private Either<ToscaRepresentation, ResponseFormat> fetchToscaRepresentation(Component component, boolean getFromCS,
254 ArtifactDefinition artifactDef, boolean isSkipImports) {
255 LifecycleStateEnum lifecycleState = component.getLifecycleState();
256 boolean shouldBeFetchedFromCassandra =
257 getFromCS || !(lifecycleState == LifecycleStateEnum.NOT_CERTIFIED_CHECKIN || lifecycleState == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
258 Either<ToscaRepresentation, ResponseFormat> toscaRepresentation =
259 shouldBeFetchedFromCassandra ? fetchToscaRepresentation(artifactDef) : generateToscaRepresentation(component, isSkipImports);
260 return toscaRepresentation.left()
261 .bind(iff(myd -> !myd.getDependencies().isDefined(), myd -> fetchToscaTemplateDependencies(myd.getMainYaml(), component)));
264 private Either<ToscaRepresentation, ResponseFormat> fetchToscaTemplateDependencies(byte[] mainYml, Component component) {
265 return toscaExportUtils.getDependencies(component).right().map(toscaError -> {
266 LOGGER.debug("Failed to retrieve dependencies for component {}, error {}", component.getUniqueId(), toscaError);
267 return componentsUtils.getResponseFormat(componentsUtils.convertFromToscaError(toscaError));
268 }).left().map(tt -> ToscaRepresentation.make(mainYml, tt));
271 private Either<ToscaRepresentation, ResponseFormat> fetchToscaRepresentation(ArtifactDefinition artifactDef) {
272 return getFromCassandra(artifactDef.getEsId()).right().map(as -> {
273 LOGGER.debug(ARTIFACT_NAME_UNIQUE_ID, artifactDef.getArtifactName(), artifactDef.getUniqueId());
274 return componentsUtils.getResponseFormat(as);
275 }).left().map(ToscaRepresentation::make);
278 private Either<byte[], ActionStatus> getFromCassandra(String cassandraId) {
279 return artifactCassandraDao.getArtifact(cassandraId).right().map(operationstatus -> {
280 LOGGER.info("Failed to fetch artifact from Cassandra by id {} error {}.", cassandraId, operationstatus);
281 StorageOperationStatus storageStatus = DaoStatusConverter.convertCassandraStatusToStorageStatus(operationstatus);
282 return componentsUtils.convertFromStorageResponse(storageStatus);
283 }).left().map(DAOArtifactData::getDataAsArray);
286 private static <L, R> F<L, Either<L, R>> iff(Predicate<L> p, Function<L, Either<L, R>> ifTrue) {
287 return l -> p.test(l) ? ifTrue.apply(l) : Either.left(l);
290 private static <A, B> F<A, B> iff(Predicate<A> p, Supplier<B> s, Function<A, B> orElse) {
291 return a -> p.test(a) ? s.get() : orElse.apply(a);
294 private void addServiceMf(Component component, ZipOutputStream zip, LifecycleStateEnum lifecycleState, boolean isInCertificationRequest,
295 String fileName, byte[] mainYaml, String definitionsPath) throws IOException {
297 if ((component.getComponentType() == ComponentTypeEnum.SERVICE) && (lifecycleState != LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
298 String serviceName = component.getName();
299 String createdBy = component.getCreatorUserId();
300 String serviceVersion;
301 if (isInCertificationRequest) {
302 int tmp = Integer.valueOf(component.getVersion().split("\\.")[0]) + 1;
303 serviceVersion = String.valueOf(tmp) + ".0";
305 serviceVersion = component.getVersion();
307 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
308 format.setTimeZone(TimeZone.getTimeZone("UTC"));
309 Date date = new Date();
310 String releaseTime = format.format(date);
311 if (component.getCategories() == null || component.getCategories().get(0) == null) {
314 String serviceType = component.getCategories().get(0).getName();
315 String description = component.getDescription();
316 String serviceTemplate = definitionsPath + fileName;
317 String hash = GeneralUtility.calculateMD5Base64EncodedByByteArray(mainYaml);
318 String nsMfBlock0 = createNsMfBlock0(serviceName, createdBy, serviceVersion, releaseTime, serviceType, description, serviceTemplate,
320 byte[] nsMfBlock0Byte = nsMfBlock0.getBytes();
321 zip.putNextEntry(new ZipEntry(SERVICE_MANIFEST));
322 zip.write(nsMfBlock0Byte);
326 private String createNsMfBlock0(String serviceName, String createdBy, String serviceVersion, String releaseTime, String serviceType,
327 String description, String serviceTemplate, String hash) {
328 final String block0template = "metadata??\n" + "ns_product_name: %s\n" + "ns_provider_id: %s\n" + "ns_package_version: %s\n" +
329 "ns_release_data_time: %s\n" + "ns_type: %s\n" + "ns_package_description: %s\n\n" + "Source: %s\n" + "Algorithm: MD5\n" + "Hash: %s\n\n";
330 return String.format(block0template, serviceName, createdBy, serviceVersion, releaseTime, serviceType, description, serviceTemplate, hash);
333 private boolean hasToWriteComponentSubstitutionType(final Component component) {
334 final Map<String, CategoryBaseTypeConfig> serviceNodeTypesConfig =
335 ConfigurationManager.getConfigurationManager().getConfiguration().getServiceBaseNodeTypes();
336 List<CategoryDefinition> categories = component.getCategories();
337 if (CollectionUtils.isNotEmpty(categories) && MapUtils.isNotEmpty(serviceNodeTypesConfig)
338 && serviceNodeTypesConfig.get(categories.get(0).getName()) != null) {
339 boolean doNotExtendBaseType = serviceNodeTypesConfig.get(categories.get(0).getName()).isDoNotExtendBaseType();
340 if (doNotExtendBaseType) {
344 if (component instanceof Service) {
345 return !ModelConverter.isAtomicComponent(component) && ((Service) component).isSubstituteCandidate();
347 return !ModelConverter.isAtomicComponent(component);
350 private Either<ZipOutputStream, ResponseFormat> writeComponentInterface(Either<ToscaRepresentation, ToscaError> interfaceRepresentation,
351 ZipOutputStream zip, String fileName, String definitionsPath) {
352 // TODO: This should not be done but we need this to keep the refactoring small enough to be easily reviewable
353 return writeComponentInterface(interfaceRepresentation, fileName, ZipWriter.live(zip), definitionsPath)
354 .map(void0 -> Either.<ZipOutputStream, ResponseFormat>left(zip)).recover(th -> {
355 LOGGER.error("#writeComponentInterface - zip writing failed with error: ", th);
356 return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
360 private Try<Void> writeComponentInterface(
361 Either<ToscaRepresentation, ToscaError> interfaceRepresentation, String fileName, ZipWriter zw, String definitionsPath) {
362 Either<byte[], ToscaError> yml = interfaceRepresentation.left()
363 .map(ToscaRepresentation::getMainYaml);
364 return fromEither(yml, ToscaErrorException::new).flatMap(zw.write(definitionsPath + ToscaExportHandler.getInterfaceFilename(fileName)));
367 private List<Triple<String, String, Component>> writeComponentInterface(final Component component, final ZipOutputStream zip,
368 final String fileName, final String definitionsPath) {
369 final Either<ToscaRepresentation, ToscaError> interfaceRepresentation = toscaExportUtils.exportComponentInterface(component, false);
370 writeComponentInterface(interfaceRepresentation, zip, fileName, definitionsPath);
371 return interfaceRepresentation.left().value().getDependencies().getOrElse(new ArrayList<>());
374 private Either<ZipOutputStream, ResponseFormat> getZipOutputStreamResponseFormatEither(ZipOutputStream zip,
375 List<Triple<String, String, Component>> dependencies,
376 String definitionsPath)
379 innerComponentsCache = ComponentCache.overwritable(overwriteIfSameVersions()).onMerge((oldValue, newValue) ->
380 LOGGER.warn("Overwriting component invariantID {} of version {} with a newer version {}", oldValue.getId(),
381 oldValue.getComponentVersion(),
382 newValue.getComponentVersion()));
383 if (dependencies != null && !dependencies.isEmpty()) {
384 for (Triple<String, String, Component> d : dependencies) {
385 String cassandraId = d.getMiddle();
386 Component childComponent = d.getRight();
387 Either<byte[], ResponseFormat> entryData = getEntryData(cassandraId, childComponent).right()
388 .map(componentsUtils::getResponseFormat);
389 if (entryData.isRight()) {
390 return Either.right(entryData.right().value());
392 //fill innerComponentsCache
393 String fileName = d.getLeft();
394 innerComponentsCache.put(cassandraId, fileName, childComponent);
395 addInnerComponentsToCache(innerComponentsCache, childComponent);
397 //add inner components to CSAR
398 return addInnerComponentsToCSAR(zip, innerComponentsCache, definitionsPath);
403 private Either<ZipOutputStream, ResponseFormat> addInnerComponentsToCSAR(ZipOutputStream zip, ComponentCache innerComponentsCache,
404 String definitionsPath)
406 for (ImmutableTriple<String, String, Component> ict : innerComponentsCache.iterable()) {
407 Component innerComponent = ict.getRight();
408 String icFileName = ict.getMiddle();
409 // add component to zip
410 Either<Tuple2<byte[], ZipEntry>, ResponseFormat> zipEntry = toZipEntry(ict, definitionsPath);
411 // TODO: this should not be done, we should instead compose this either further,
413 // but in order to keep this refactoring small, we'll stop here.
414 if (zipEntry.isRight()) {
415 return Either.right(zipEntry.right().value());
417 Tuple2<byte[], ZipEntry> value = zipEntry.left().value();
418 zip.putNextEntry(value._2);
420 // add component interface to zip
421 if (hasToWriteComponentSubstitutionType(innerComponent)) {
422 writeComponentInterface(innerComponent, zip, icFileName, definitionsPath);
428 private Either<Tuple2<byte[], ZipEntry>, ResponseFormat> toZipEntry(ImmutableTriple<String, String, Component> cachedEntry,
429 String definitionsPath) {
430 String cassandraId = cachedEntry.getLeft();
431 String fileName = cachedEntry.getMiddle();
432 Component innerComponent = cachedEntry.getRight();
433 return getEntryData(cassandraId, innerComponent).right().map(status -> {
434 LOGGER.debug("Failed adding to zip component {}, error {}", cassandraId, status);
435 return componentsUtils.getResponseFormat(status);
436 }).left().map(content -> new Tuple2<>(content, new ZipEntry(definitionsPath + fileName)));
439 private void addInnerComponentsToCache(ComponentCache componentCache, Component childComponent) {
440 javaListToVavrList(childComponent.getComponentInstances()).filter(ci -> componentCache.notCached(ci.getComponentUid())).forEach(ci -> {
441 // all resource must be only once!
442 Either<Resource, StorageOperationStatus> resource = toscaOperationFacade.getToscaElement(ci.getComponentUid());
443 Component componentRI = checkAndAddComponent(componentCache, ci, resource);
444 //if not atomic - insert inner components as well
446 // TODO: This could potentially create a StackOverflowException if the call stack
448 // happens to be too large. Tail-recursive optimization should be used here.
449 if (!ModelConverter.isAtomicComponent(componentRI)) {
450 addInnerComponentsToCache(componentCache, componentRI);
455 private Component checkAndAddComponent(ComponentCache componentCache, ComponentInstance ci, Either<Resource, StorageOperationStatus> resource) {
456 if (resource.isRight()) {
457 LOGGER.debug("Failed to fetch resource with id {} for instance {}", ci.getComponentUid(), ci.getName());
459 Component componentRI = resource.left().value();
460 Map<String, ArtifactDefinition> childToscaArtifacts = componentRI.getToscaArtifacts();
461 ArtifactDefinition childArtifactDefinition = childToscaArtifacts.get(ToscaExportHandler.ASSET_TOSCA_TEMPLATE);
462 if (childArtifactDefinition != null) {
464 componentCache.put(childArtifactDefinition.getEsId(), childArtifactDefinition.getArtifactName(), componentRI);
469 private Either<byte[], ActionStatus> getEntryData(String cassandraId, Component childComponent) {
470 if (cassandraId == null || cassandraId.isEmpty()) {
471 return toscaExportUtils.exportComponent(childComponent).right().map(toscaErrorToActionStatus(childComponent)).left()
472 .map(ToscaRepresentation::getMainYaml);
474 return getFromCassandra(cassandraId);
478 private F<ToscaError, ActionStatus> toscaErrorToActionStatus(Component childComponent) {
479 return toscaError -> {
480 LOGGER.debug("Failed to export tosca template for child component {} error {}", childComponent.getUniqueId(), toscaError);
481 return componentsUtils.convertFromToscaError(toscaError);
485 private Either<byte[], ResponseFormat> getLatestSchemaFilesFromCassandra() {
486 String fto = versionFirstThreeOctets;
487 return sdcSchemaFilesCassandraDao.getSpecificSchemaFiles(fto, CONFORMANCE_LEVEL).right().map(schemaFilesFetchDBError(fto)).left()
488 .bind(iff(List::isEmpty, () -> schemaFileFetchError(fto), s -> Either.left(s.iterator().next().getPayloadAsArray())));
491 private F<CassandraOperationStatus, ResponseFormat> schemaFilesFetchDBError(String firstThreeOctets) {
493 LOGGER.debug("Failed to get the schema files SDC-Version: {} Conformance-Level {}. Please fix DB table accordingly.", firstThreeOctets,
495 StorageOperationStatus sos = DaoStatusConverter.convertCassandraStatusToStorageStatus(cos);
496 return componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(sos));
500 private Either<byte[], ResponseFormat> schemaFileFetchError(String firstThreeOctets) {
501 LOGGER.debug("Failed to get the schema files SDC-Version: {} Conformance-Level {}", firstThreeOctets, CONFORMANCE_LEVEL);
502 return Either.right(componentsUtils.getResponseFormat(ActionStatus.TOSCA_SCHEMA_FILES_NOT_FOUND, firstThreeOctets, CONFORMANCE_LEVEL));
506 * Create a list of all derived nodes found on the package
508 * @param dependencies all node dependencies
509 * @return a list of nodes
511 private List<String> findNonRootNodesFromPackage(final List<Triple<String, String, Component>> dependencies) {
512 final List<String> nodes = new ArrayList<>();
513 if (CollectionUtils.isNotEmpty(dependencies)) {
514 final String NATIVE_ROOT = "tosca.nodes.Root";
515 dependencies.forEach(dependency -> {
516 if (dependency.getRight() instanceof Resource) {
517 final Resource resource = (Resource) dependency.getRight();
518 if (CollectionUtils.isNotEmpty(resource.getDerivedList())) {
519 resource.getDerivedList().stream().filter(node -> !nodes.contains(node) && !NATIVE_ROOT.equalsIgnoreCase(node))
520 .forEach(node -> nodes.add(node));
529 * Writes to a CSAR zip from casandra schema data
531 * @param zipOutputStream stores the input stream content
532 * @param schemaFileZip zip data from Cassandra
533 * @param nodesFromPackage list of all nodes found on the onboarded package
535 private void addSchemaFilesFromCassandra(final ZipOutputStream zipOutputStream, final byte[] schemaFileZip, final List<String> nodesFromPackage,
536 final String definitionsPath) {
537 final int initSize = 2048;
538 LOGGER.debug("Starting copy from Schema file zip to CSAR zip");
539 try (final ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(
540 schemaFileZip)); final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
541 byteArrayOutputStream, initSize)) {
543 while ((entry = zipInputStream.getNextEntry()) != null) {
544 ZipUtils.checkForZipSlipInRead(entry);
545 final String entryName = entry.getName();
546 int readSize = initSize;
547 final byte[] entryData = new byte[initSize];
548 if (shouldZipEntryBeHandled(entryName)) {
549 if (NODES_YML.equalsIgnoreCase(entryName)) {
550 handleNode(zipInputStream, byteArrayOutputStream, nodesFromPackage);
552 while ((readSize = zipInputStream.read(entryData, 0, readSize)) != -1) {
553 bufferedOutputStream.write(entryData, 0, readSize);
555 bufferedOutputStream.flush();
557 byteArrayOutputStream.flush();
558 zipOutputStream.putNextEntry(new ZipEntry(definitionsPath + entryName));
559 zipOutputStream.write(byteArrayOutputStream.toByteArray());
560 zipOutputStream.flush();
561 byteArrayOutputStream.reset();
564 } catch (final Exception e) {
565 LOGGER.error("Error while writing the SDC schema file to the CSAR", e);
566 throw new ByResponseFormatComponentException(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
568 LOGGER.debug("Finished copy from Schema file zip to CSAR zip");
572 * Handles the nodes.yml zip entry, updating the nodes.yml to avoid duplicated nodes on it.
574 * @param zipInputStream the zip entry to be read
575 * @param byteArrayOutputStream an output stream in which the data is written into a byte array.
576 * @param nodesFromPackage list of all nodes found on the onboarded package
578 private void handleNode(final ZipInputStream zipInputStream, final ByteArrayOutputStream byteArrayOutputStream,
579 final List<String> nodesFromPackage) throws IOException {
580 final Map<String, Object> nodesFromArtifactFile = readYamlZipEntry(zipInputStream);
581 final Map<String, Object> nodesYaml = updateNodeYml(nodesFromPackage, nodesFromArtifactFile);
582 updateZipEntry(byteArrayOutputStream, nodesYaml);
586 * Updates the zip entry from the given parameters
588 * @param byteArrayOutputStream an output stream in which the data is written into a byte array.
589 * @param nodesYaml a Map of nodes to be written
591 private void updateZipEntry(final ByteArrayOutputStream byteArrayOutputStream, final Map<String, Object> nodesYaml) throws IOException {
592 if (MapUtils.isNotEmpty(nodesYaml)) {
593 byteArrayOutputStream.write(new YamlUtil().objectToYaml(nodesYaml).getBytes());
598 * Filters and removes all duplicated nodes found
600 * @param nodesFromPackage a List of all derived nodes found on the given package
601 * @param nodesFromArtifactFile represents the nodes.yml file stored in Cassandra
602 * @return a nodes Map updated
604 private Map<String, Object> updateNodeYml(final List<String> nodesFromPackage, final Map<String, Object> nodesFromArtifactFile) {
605 if (MapUtils.isNotEmpty(nodesFromArtifactFile)) {
606 final String nodeTypeBlock = TypeUtils.ToscaTagNamesEnum.NODE_TYPES.getElementName();
607 final Map<String, Object> nodeTypes = (Map<String, Object>) nodesFromArtifactFile.get(nodeTypeBlock);
608 nodesFromPackage.stream().filter(nodeTypes::containsKey).forEach(nodeTypes::remove);
609 nodesFromArtifactFile.replace(nodeTypeBlock, nodeTypes);
611 return nodesFromArtifactFile;
615 * Writes a new zip entry
617 * @param zipInputStream the zip entry to be read
618 * @return a map of the given zip entry
620 private Map<String, Object> readYamlZipEntry(final ZipInputStream zipInputStream) throws IOException {
621 final int initSize = 2048;
622 final StringBuilder zipEntry = new StringBuilder();
623 final byte[] buffer = new byte[initSize];
625 while ((read = zipInputStream.read(buffer, 0, initSize)) >= 0) {
626 zipEntry.append(new String(buffer, 0, read));
628 return (Map<String, Object>) new Yaml().load(zipEntry.toString());
632 * Checks if the zip entry should or should not be added to the CSAR based on the given global type list
634 * @param entryName the zip entry name
635 * @return true if the zip entry should be handled
637 private boolean shouldZipEntryBeHandled(final String entryName) {
638 return ConfigurationManager.getConfigurationManager().getConfiguration().getGlobalCsarImports().stream()
639 .anyMatch(entry -> entry.contains(entryName));
642 private void addSchemaFilesByModel(final ZipOutputStream zipOutputStream, final String modelName,
643 final String definitionsPath, final boolean isSingleImportsFile) {
645 final List<ToscaImportByModel> modelDefaultImportList = modelOperation.findAllModelImports(modelName, true);
646 final Set<Path> writtenEntryPathList = new HashSet<>();
647 final var defsPath = Path.of(definitionsPath);
648 Map<Path, byte[]> contentToMerge = new HashMap<>();
649 for (final ToscaImportByModel toscaImportByModel : modelDefaultImportList) {
650 var importPath = Path.of(toscaImportByModel.getFullPath());
651 if (!isSingleImportsFile) {
652 if (ADDITIONAL_TYPE_DEFINITIONS.equals(Paths.get(String.valueOf(importPath)).normalize().toString())) {
653 final Path entryPath = defsPath.resolve(importPath);
654 contentToMerge.put(entryPath, toscaImportByModel.getContent().getBytes(StandardCharsets.UTF_8));
656 if (writtenEntryPathList.contains(defsPath.resolve(importPath))) {
657 importPath = ToscaDefaultImportHelper.addModelAsFilePrefix(importPath, toscaImportByModel.getModelId());
659 final Path entryPath = defsPath.resolve(importPath);
660 writtenEntryPathList.add(entryPath);
661 contentToMerge.put(entryPath, toscaImportByModel.getContent().getBytes(StandardCharsets.UTF_8));
664 if (writtenEntryPathList.contains(defsPath.resolve(importPath))) {
665 importPath = ToscaDefaultImportHelper.addModelAsFilePrefix(importPath, toscaImportByModel.getModelId());
667 final Path entryPath = defsPath.resolve(importPath);
668 zipOutputStream.putNextEntry(new ZipEntry(entryPath.toString()));
669 writtenEntryPathList.add(entryPath);
670 final byte[] content = toscaImportByModel.getContent().getBytes(StandardCharsets.UTF_8);
671 zipOutputStream.write(content, 0, content.length);
672 zipOutputStream.closeEntry();
675 if (!isSingleImportsFile) {
676 byte[] mergingContent = new byte[0];
677 for (Map.Entry<Path, byte[]> entry : contentToMerge.entrySet()) {
678 if (ADDITIONAL_TYPE_DEFINITIONS.equals(Paths.get(String.valueOf(entry.getKey())).normalize().toString())) {
679 mergingContent = mergeContent(mergingContent, entry.getValue());
681 final var zipEntry = new ZipEntry(entry.getKey().toString());
682 zipOutputStream.putNextEntry(zipEntry);
683 writtenEntryPathList.add(entry.getKey());
684 mergingContent = mergeContent(mergingContent, entry.getValue());
685 zipOutputStream.write(mergingContent, 0, mergingContent.length);
686 zipOutputStream.closeEntry();
690 } catch (final IOException e) {
691 LOGGER.error(String.valueOf(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR), CsarUtils.class.getName(),
692 "Error while writing the schema files by model to the CSAR", e);
693 throw new ByResponseFormatComponentException(componentsUtils.getResponseFormat(ActionStatus.CSAR_TOSCA_IMPORTS_ERROR));
697 private byte[] mergeContent(final byte[] first, final byte[] second) {
698 byte[] merged = new byte[0];
699 final Map<String, Object> firstMap = new Yaml().load(new String(first));
700 final Map<String, Object> secondMap = new Yaml().load(new String(second));
701 if (MapUtils.isNotEmpty(secondMap)) {
702 final DumperOptions options = new DumperOptions();
703 options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
704 final Yaml yaml = new Yaml(options);
705 for (final Entry<String, Object> secondMapEntry : secondMap.entrySet()) {
706 final Map<String, Object> newMap = new HashMap<>();
707 if (secondMapEntry.getKey().endsWith("_types")) {
708 if (MapUtils.isNotEmpty(firstMap) && firstMap.containsKey(secondMapEntry.getKey())) {
709 final Map<String, Object> secondMapEntryValue = (Map<String, Object>) secondMapEntry.getValue();
710 final Map<String, Object> firstMapValue = (Map<String, Object>) firstMap.get(secondMapEntry.getKey());
711 secondMapEntryValue.putAll(firstMapValue);
712 newMap.put(secondMapEntry.getKey(), secondMapEntryValue);
714 newMap.put(secondMapEntry.getKey(), secondMapEntry.getValue());
717 newMap.put(secondMapEntry.getKey(), secondMapEntry.getValue());
719 merged = Bytes.concat(merged, yaml.dumpAsMap(newMap).getBytes());
725 private Either<CsarDefinition, ResponseFormat> collectComponentCsarDefinition(Component component) {
726 ComponentArtifacts componentArtifacts = new ComponentArtifacts();
727 Component updatedComponent = component;
729 //get service to receive the AII artifacts uploaded to the service
730 if (updatedComponent.getComponentType() == ComponentTypeEnum.SERVICE) {
731 Either<Service, StorageOperationStatus> getServiceResponse = toscaOperationFacade.getToscaElement(updatedComponent.getUniqueId());
733 if (getServiceResponse.isRight()) {
734 ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getServiceResponse.right().value());
735 return Either.right(componentsUtils.getResponseFormat(actionStatus));
738 updatedComponent = getServiceResponse.left().value();
741 //find the artifacts of the main component, it would have its composed instances artifacts in a separate folder
742 ComponentTypeArtifacts componentInstanceArtifacts = new ComponentTypeArtifacts();
743 ArtifactsInfo artifactsInfo = collectComponentArtifacts(updatedComponent);
744 componentInstanceArtifacts.setComponentArtifacts(artifactsInfo);
745 componentArtifacts.setMainTypeAndCIArtifacts(componentInstanceArtifacts);
747 Map<String, ComponentTypeArtifacts> resourceTypeArtifacts = componentArtifacts
748 .getComponentTypeArtifacts(); //artifacts mapped by the component type(tosca name+version)
749 //get the component instances
750 List<ComponentInstance> componentInstances = updatedComponent.getComponentInstances();
751 if (componentInstances != null) {
752 for (ComponentInstance componentInstance : componentInstances) {
753 //call recursive to find artifacts for all the path
754 Either<Boolean, ResponseFormat> collectComponentInstanceArtifacts = collectComponentInstanceArtifacts(
755 updatedComponent, componentInstance, resourceTypeArtifacts, componentInstanceArtifacts);
756 if (collectComponentInstanceArtifacts.isRight()) {
757 return Either.right(collectComponentInstanceArtifacts.right().value());
762 if (LOGGER.isDebugEnabled()) {
763 printResult(componentArtifacts, updatedComponent.getName());
766 return Either.left(new CsarDefinition(componentArtifacts));
769 private void printResult(ComponentArtifacts componentArtifacts, String name) {
770 StringBuilder result = new StringBuilder();
771 result.append("Artifacts of main component " + name + "\n");
772 ComponentTypeArtifacts componentInstanceArtifacts = componentArtifacts.getMainTypeAndCIArtifacts();
773 printArtifacts(componentInstanceArtifacts);
774 result.append("Type Artifacts\n");
775 for (Map.Entry<String, ComponentTypeArtifacts> typeArtifacts : componentArtifacts.getComponentTypeArtifacts().entrySet()) {
776 result.append("Folder " + typeArtifacts.getKey() + "\n");
777 result.append(printArtifacts(typeArtifacts.getValue()));
780 if (LOGGER.isDebugEnabled()) {
781 LOGGER.debug(result.toString());
785 private String printArtifacts(ComponentTypeArtifacts componentInstanceArtifacts) {
786 StringBuilder result = new StringBuilder();
787 ArtifactsInfo artifactsInfo = componentInstanceArtifacts.getComponentArtifacts();
788 Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> componentArtifacts = artifactsInfo.getArtifactsInfo();
789 printArtifacts(componentArtifacts);
790 result = result.append("Resources\n");
791 for (Map.Entry<String, ArtifactsInfo> resourceInstance : componentInstanceArtifacts.getComponentInstancesArtifacts().entrySet()) {
792 result.append("Folder" + resourceInstance.getKey() + "\n");
793 result.append(printArtifacts(resourceInstance.getValue().getArtifactsInfo()));
796 return result.toString();
799 private String printArtifacts(Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> componetArtifacts) {
800 StringBuilder result = new StringBuilder();
801 for (Map.Entry<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> artifactGroup : componetArtifacts.entrySet()) {
802 result.append(" " + artifactGroup.getKey().getType());
803 for (Map.Entry<String, List<ArtifactDefinition>> groupArtifacts : artifactGroup.getValue().entrySet()) {
804 result.append(" " + groupArtifacts.getKey());
805 for (ArtifactDefinition artifact : groupArtifacts.getValue()) {
806 result.append(" " + artifact.getArtifactDisplayName());
811 return result.toString();
814 private Either<Boolean, ResponseFormat> collectComponentInstanceArtifacts(Component parentComponent, ComponentInstance componentInstance,
815 Map<String, ComponentTypeArtifacts> resourcesTypeArtifacts,
816 ComponentTypeArtifacts instanceArtifactsLocation) {
817 //1. get the component instance component
819 if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy) {
820 componentUid = componentInstance.getSourceModelUid();
822 componentUid = componentInstance.getComponentUid();
824 Either<Component, StorageOperationStatus> component = toscaOperationFacade.getToscaElement(componentUid);
825 if (component.isRight()) {
826 LOGGER.error("Failed to fetch resource with id {} for instance {}", componentUid, parentComponent.getUUID());
827 return Either.right(componentsUtils.getResponseFormat(ActionStatus.ASSET_NOT_FOUND_DURING_CSAR_CREATION,
828 parentComponent.getComponentType().getValue(), parentComponent.getUUID(),
829 componentInstance.getOriginType().getComponentType().getValue(), componentUid));
831 Component fetchedComponent = component.left().value();
833 //2. fill the artifacts for the current component parent type
834 String toscaComponentName =
835 componentInstance.getToscaComponentName() + "_v" + componentInstance.getComponentVersion();
837 // if there are no artifacts for this component type we need to fetch and build them
838 ComponentTypeArtifacts componentParentArtifacts = Optional
839 .ofNullable(resourcesTypeArtifacts.get(toscaComponentName))
840 .orElseGet(() -> collectComponentTypeArtifacts(fetchedComponent));
842 if (componentParentArtifacts.getComponentArtifacts().isNotEmpty()) {
843 resourcesTypeArtifacts.put(toscaComponentName, componentParentArtifacts);
846 //3. find the artifacts specific to the instance
847 Map<String, List<ArtifactDefinition>> componentInstanceSpecificInformationalArtifacts =
848 getComponentInstanceSpecificArtifacts(componentInstance.getArtifacts(),
849 componentParentArtifacts.getComponentArtifacts().getArtifactsInfo(), ArtifactGroupTypeEnum.INFORMATIONAL);
850 Map<String, List<ArtifactDefinition>> componentInstanceSpecificDeploymentArtifacts =
851 getComponentInstanceSpecificArtifacts(componentInstance.getDeploymentArtifacts(),
852 componentParentArtifacts.getComponentArtifacts().getArtifactsInfo(), ArtifactGroupTypeEnum.DEPLOYMENT);
854 //4. add the instances artifacts to the component type
855 ArtifactsInfo artifactsInfo = new ArtifactsInfo();
856 if (!componentInstanceSpecificInformationalArtifacts.isEmpty()) {
857 artifactsInfo.addArtifactsToGroup(ArtifactGroupTypeEnum.INFORMATIONAL, componentInstanceSpecificInformationalArtifacts);
859 if (!componentInstanceSpecificDeploymentArtifacts.isEmpty()) {
860 artifactsInfo.addArtifactsToGroup(ArtifactGroupTypeEnum.DEPLOYMENT, componentInstanceSpecificDeploymentArtifacts);
862 if (!artifactsInfo.isEmpty()) {
863 instanceArtifactsLocation.addComponentInstancesArtifacts(componentInstance.getNormalizedName(), artifactsInfo);
866 //5. do the same for all the component instances
867 List<ComponentInstance> componentInstances = fetchedComponent.getComponentInstances();
868 if (componentInstances != null) {
869 for (ComponentInstance childComponentInstance : componentInstances) {
870 Either<Boolean, ResponseFormat> collectComponentInstanceArtifacts = collectComponentInstanceArtifacts(
871 fetchedComponent, childComponentInstance, resourcesTypeArtifacts, componentParentArtifacts);
872 if (collectComponentInstanceArtifacts.isRight()) {
873 return collectComponentInstanceArtifacts;
878 return Either.left(true);
881 private Map<String, List<ArtifactDefinition>> getComponentInstanceSpecificArtifacts(Map<String, ArtifactDefinition> componentArtifacts,
882 Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> componentTypeArtifacts,
883 ArtifactGroupTypeEnum artifactGroupTypeEnum) {
884 Map<String, List<ArtifactDefinition>> parentArtifacts = componentTypeArtifacts
885 .get(artifactGroupTypeEnum); //the artfiacts of the component itself and not the instance
887 Map<String, List<ArtifactDefinition>> artifactsByTypeOfComponentInstance = new HashMap<>();
888 if (componentArtifacts != null) {
889 for (ArtifactDefinition artifact : componentArtifacts.values()) {
890 List<ArtifactDefinition> parentArtifactsByType = null;
891 if (parentArtifacts != null) {
892 parentArtifactsByType = parentArtifacts.get(artifact.getArtifactType());
894 //the artifact is of instance
895 if (parentArtifactsByType == null || !parentArtifactsByType.contains(artifact)) {
896 List<ArtifactDefinition> typeArtifacts = artifactsByTypeOfComponentInstance.get(artifact.getArtifactType());
897 if (typeArtifacts == null) {
898 typeArtifacts = new ArrayList<>();
899 artifactsByTypeOfComponentInstance.put(artifact.getArtifactType(), typeArtifacts);
901 typeArtifacts.add(artifact);
906 return artifactsByTypeOfComponentInstance;
909 private ComponentTypeArtifacts collectComponentTypeArtifacts(Component fetchedComponent) {
910 ArtifactsInfo componentArtifacts = collectComponentArtifacts(fetchedComponent);
911 ComponentTypeArtifacts componentArtifactsInfo = new ComponentTypeArtifacts();
912 if (componentArtifacts.isNotEmpty()) {
913 componentArtifactsInfo.setComponentArtifacts(componentArtifacts);
915 return componentArtifactsInfo;
918 private ArtifactsInfo collectComponentArtifacts(Component component) {
919 Map<String, ArtifactDefinition> informationalArtifacts = component.getArtifacts();
920 Map<String, List<ArtifactDefinition>> informationalArtifactsByType = collectGroupArtifacts(informationalArtifacts);
921 Map<String, ArtifactDefinition> deploymentArtifacts = component.getDeploymentArtifacts();
922 Map<String, List<ArtifactDefinition>> deploymentArtifactsByType = collectGroupArtifacts(deploymentArtifacts);
923 ArtifactsInfo artifactsInfo = new ArtifactsInfo();
924 if (!informationalArtifactsByType.isEmpty()) {
925 artifactsInfo.addArtifactsToGroup(ArtifactGroupTypeEnum.INFORMATIONAL, informationalArtifactsByType);
927 if (!deploymentArtifactsByType.isEmpty()) {
928 artifactsInfo.addArtifactsToGroup(ArtifactGroupTypeEnum.DEPLOYMENT, deploymentArtifactsByType);
931 return artifactsInfo;
934 private Map<String, List<ArtifactDefinition>> collectGroupArtifacts(
935 final Map<String, ArtifactDefinition> componentArtifacts) {
936 final Map<String, List<ArtifactDefinition>> artifactsByType = new HashMap<>();
937 for (final ArtifactDefinition artifact : componentArtifacts.values()) {
938 if (artifact.getArtifactUUID() != null) {
939 artifactsByType.putIfAbsent(artifact.getArtifactType(), new ArrayList<>());
940 final List<ArtifactDefinition> typeArtifacts = artifactsByType.get(artifact.getArtifactType());
941 typeArtifacts.add(artifact);
944 return artifactsByType;
947 private Either<ZipOutputStream, ResponseFormat> writeAllFilesToCsar(Component mainComponent, CsarDefinition csarDefinition,
948 ZipOutputStream zipstream, boolean isInCertificationRequest)
950 ComponentArtifacts componentArtifacts = csarDefinition.getComponentArtifacts();
951 Either<ZipOutputStream, ResponseFormat> writeComponentArtifactsToSpecifiedPath = writeComponentArtifactsToSpecifiedPath(mainComponent,
952 componentArtifacts, zipstream, ARTIFACTS_PATH, isInCertificationRequest);
953 if (writeComponentArtifactsToSpecifiedPath.isRight()) {
954 return Either.right(writeComponentArtifactsToSpecifiedPath.right().value());
956 ComponentTypeArtifacts mainTypeAndCIArtifacts = componentArtifacts.getMainTypeAndCIArtifacts();
957 writeComponentArtifactsToSpecifiedPath = writeArtifactsInfoToSpecifiedPath(mainComponent, mainTypeAndCIArtifacts.getComponentArtifacts(),
958 zipstream, ARTIFACTS_PATH, isInCertificationRequest);
959 if (writeComponentArtifactsToSpecifiedPath.isRight()) {
960 return Either.right(writeComponentArtifactsToSpecifiedPath.right().value());
962 Map<String, ArtifactsInfo> componentInstancesArtifacts = mainTypeAndCIArtifacts.getComponentInstancesArtifacts();
963 String currentPath = ARTIFACTS_PATH + RESOURCES_PATH;
964 for (String keyAssetName : componentInstancesArtifacts.keySet()) {
965 ArtifactsInfo artifactsInfo = componentInstancesArtifacts.get(keyAssetName);
966 String pathWithAssetName = currentPath + keyAssetName + PATH_DELIMITER;
967 writeComponentArtifactsToSpecifiedPath = writeArtifactsInfoToSpecifiedPath(mainComponent, artifactsInfo, zipstream, pathWithAssetName,
968 isInCertificationRequest);
969 if (writeComponentArtifactsToSpecifiedPath.isRight()) {
970 return Either.right(writeComponentArtifactsToSpecifiedPath.right().value());
973 writeComponentArtifactsToSpecifiedPath = writeOperationsArtifactsToCsar(mainComponent, zipstream);
974 if (writeComponentArtifactsToSpecifiedPath.isRight()) {
975 return Either.right(writeComponentArtifactsToSpecifiedPath.right().value());
977 return Either.left(zipstream);
980 private Either<ZipOutputStream, ResponseFormat> writeOperationsArtifactsToCsar(Component component, ZipOutputStream zipstream) {
981 if (checkComponentBeforeOperation(component)) {
982 return Either.left(zipstream);
984 for (Map.Entry<String, InterfaceDefinition> interfaceEntry : ((Resource) component).getInterfaces().entrySet()) {
985 for (OperationDataDefinition operation : interfaceEntry.getValue().getOperations().values()) {
987 if (checkComponentBeforeWrite(component, interfaceEntry, operation)) {
990 final String artifactUUID = operation.getImplementation().getArtifactUUID();
991 if (artifactUUID == null) {
994 final Either<byte[], ActionStatus> artifactFromCassandra = getFromCassandra(artifactUUID);
995 final String artifactName = operation.getImplementation().getArtifactName();
996 if (artifactFromCassandra.isRight()) {
997 LOGGER.error(ARTIFACT_NAME_UNIQUE_ID, artifactName, artifactUUID);
998 LOGGER.error("Failed to get {} payload from DB reason: {}", artifactName, artifactFromCassandra.right().value());
999 return Either.right(componentsUtils.getResponseFormat(
1000 ARTIFACT_PAYLOAD_NOT_FOUND_DURING_CSAR_CREATION, "Resource", component.getUniqueId(), artifactName, artifactUUID));
1002 zipstream.putNextEntry(new ZipEntry(OperationArtifactUtil.createOperationArtifactPath(component, null, operation, true)));
1003 zipstream.write(artifactFromCassandra.left().value());
1004 } catch (IOException e) {
1005 LOGGER.error("Component Name {}, Interface Name {}, Operation Name {}", component.getNormalizedName(), interfaceEntry.getKey(),
1006 operation.getName());
1007 LOGGER.error("Error while writing the operation's artifacts to the CSAR", e);
1008 return Either.right(componentsUtils.getResponseFormat(ERROR_DURING_CSAR_CREATION, "Resource", component.getUniqueId()));
1012 return Either.left(zipstream);
1015 private boolean checkComponentBeforeWrite(Component component, Map.Entry<String, InterfaceDefinition> interfaceEntry,
1016 OperationDataDefinition operation) {
1017 final ArtifactDataDefinition implementation = operation.getImplementation();
1018 if (implementation == null) {
1019 LOGGER.debug("Component Name {}, Interface Id {}, Operation Name {} - no Operation Implementation found", component.getNormalizedName(),
1020 interfaceEntry.getValue().getUniqueId(), operation.getName());
1023 final String artifactName = implementation.getArtifactName();
1024 if (artifactName == null) {
1025 LOGGER.debug("Component Name {}, Interface Id {}, Operation Name {} - no artifact found", component.getNormalizedName(),
1026 interfaceEntry.getValue().getUniqueId(), operation.getName());
1029 if (OperationArtifactUtil.artifactNameIsALiteralValue(artifactName)) {
1030 LOGGER.debug("Component Name {}, Interface Id {}, Operation Name {} - artifact name is a literal value rather than an SDC artifact",
1031 component.getNormalizedName(), interfaceEntry.getValue().getUniqueId(), operation.getName());
1037 private boolean checkComponentBeforeOperation(Component component) {
1038 if (component instanceof Service) {
1041 if (Objects.isNull(((Resource) component).getInterfaces())) {
1042 LOGGER.debug("Component Name {}- no interfaces found", component.getNormalizedName());
1048 private Either<ZipOutputStream, ResponseFormat> writeArtifactsInfoToSpecifiedPath(final Component mainComponent,
1049 final ArtifactsInfo currArtifactsInfo,
1050 final ZipOutputStream zip, final String path,
1051 final boolean isInCertificationRequest) throws IOException {
1052 final Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> artifactsInfo = currArtifactsInfo.getArtifactsInfo();
1053 for (final ArtifactGroupTypeEnum artifactGroupTypeEnum : artifactsInfo.keySet()) {
1054 final String groupTypeFolder = path + WordUtils.capitalizeFully(artifactGroupTypeEnum.getType()) + PATH_DELIMITER;
1055 final Map<String, List<ArtifactDefinition>> artifactTypesMap = artifactsInfo.get(artifactGroupTypeEnum);
1056 for (final String artifactType : artifactTypesMap.keySet()) {
1057 final List<ArtifactDefinition> artifactDefinitionList = artifactTypesMap.get(artifactType);
1058 String artifactTypeFolder = groupTypeFolder + artifactType + PATH_DELIMITER;
1059 if (ArtifactTypeEnum.WORKFLOW.getType().equals(artifactType) && path.contains(ARTIFACTS_PATH + RESOURCES_PATH)) {
1060 // Ignore this packaging as BPMN artifacts needs to be packaged in different manner
1063 if (ArtifactTypeEnum.WORKFLOW.getType().equals(artifactType)) {
1064 artifactTypeFolder += OperationArtifactUtil.BPMN_ARTIFACT_PATH + File.separator;
1065 } else if (ArtifactTypeEnum.ONBOARDED_PACKAGE.getType().equals(artifactType)) {
1066 // renaming legacy folder ONBOARDED_PACKAGE to the new folder ETSI_PACKAGE
1067 artifactTypeFolder = artifactTypeFolder
1068 .replace(ArtifactTypeEnum.ONBOARDED_PACKAGE.getType(), ArtifactTypeEnum.ETSI_PACKAGE.getType());
1070 // TODO: We should not do this but in order to keep this refactoring small enough,
1072 // we'll leave this as is for now
1073 List<ArtifactDefinition> collect = filterArtifactDefinitionToZip(mainComponent, artifactDefinitionList, isInCertificationRequest)
1074 .collect(Collectors.toList());
1075 for (ArtifactDefinition ad : collect) {
1076 zip.putNextEntry(new ZipEntry(artifactTypeFolder + ad.getArtifactName()));
1077 zip.write(ad.getPayloadData());
1081 return Either.left(zip);
1084 private Stream<ArtifactDefinition> filterArtifactDefinitionToZip(Component mainComponent, List<ArtifactDefinition> artifactDefinitionList,
1085 boolean isInCertificationRequest) {
1086 return artifactDefinitionList.stream().filter(shouldBeInZip(isInCertificationRequest, mainComponent)).map(this::fetchPayLoadData)
1087 .filter(Either::isLeft).map(e -> e.left().value());
1090 private Predicate<ArtifactDefinition> shouldBeInZip(boolean isInCertificationRequest, Component component) {
1091 return artifactDefinition -> !(!isInCertificationRequest && component.isService() && artifactDefinition.isHeatEnvType() || artifactDefinition
1092 .hasNoMandatoryEsId());
1095 private Either<ArtifactDefinition, ActionStatus> fetchPayLoadData(ArtifactDefinition ad) {
1096 byte[] payloadData = ad.getPayloadData();
1097 if (payloadData == null) {
1098 return getFromCassandra(ad.getEsId()).left().map(pd -> {
1101 }).right().map(as -> {
1102 LOGGER.debug(ARTIFACT_NAME_UNIQUE_ID, ad.getArtifactName(), ad.getUniqueId());
1103 LOGGER.debug("Failed to get {} payload from DB reason: {}", ad.getArtifactName(), as);
1107 return Either.left(ad);
1111 private Either<ZipOutputStream, ResponseFormat> writeComponentArtifactsToSpecifiedPath(Component mainComponent,
1112 ComponentArtifacts componentArtifacts,
1113 ZipOutputStream zipstream, String currentPath,
1114 boolean isInCertificationRequest) throws IOException {
1115 Map<String, ComponentTypeArtifacts> componentTypeArtifacts = componentArtifacts.getComponentTypeArtifacts();
1118 //<Inner Asset TOSCA name (e.g. VFC name)> folder name: <Inner Asset TOSCA name (e.g. VFC name)>_v<version>.
1120 //E.g. "org.openecomp.resource.vf.vipr_atm_v1.0"
1121 Set<String> componentTypeArtifactsKeys = componentTypeArtifacts.keySet();
1122 for (String keyAssetName : componentTypeArtifactsKeys) {
1123 ComponentTypeArtifacts componentInstanceArtifacts = componentTypeArtifacts.get(keyAssetName);
1124 ArtifactsInfo componentArtifacts2 = componentInstanceArtifacts.getComponentArtifacts();
1125 String pathWithAssetName = currentPath + keyAssetName + PATH_DELIMITER;
1126 Either<ZipOutputStream, ResponseFormat> writeArtifactsInfoToSpecifiedPath = writeArtifactsInfoToSpecifiedPath(mainComponent,
1127 componentArtifacts2, zipstream, pathWithAssetName, isInCertificationRequest);
1128 if (writeArtifactsInfoToSpecifiedPath.isRight()) {
1129 return writeArtifactsInfoToSpecifiedPath;
1132 return Either.left(zipstream);
1135 private Either<ToscaRepresentation, ResponseFormat> generateToscaRepresentation(Component component, boolean isSkipImports) {
1136 return toscaExportUtils.exportComponent(component, isSkipImports).right().map(toscaError -> {
1137 LOGGER.debug("exportComponent failed {}", toscaError);
1138 return componentsUtils.getResponseFormat(componentsUtils.convertFromToscaError(toscaError));
1142 private String createToscaBlock0(String metaFileVersion, String csarVersion, String createdBy, String entryDef, boolean isAsdPackage,
1143 String definitionsPath) {
1144 final String block0template = "TOSCA-Meta-File-Version: %s\nCSAR-Version: %s\nCreated-By: %s\nEntry-Definitions: "
1145 + definitionsPath + "%s\n%s\nName: csar.meta\nContent-Type: text/plain\n";
1146 return String.format(block0template, metaFileVersion, csarVersion, createdBy, entryDef, isAsdPackage ? "entry_definition_type: asd" : "");
1149 private class CsarDefinition {
1151 private ComponentArtifacts componentArtifacts;
1153 // add list of tosca artifacts and meta describes CSAR zip root
1154 public CsarDefinition(ComponentArtifacts componentArtifacts) {
1155 this.componentArtifacts = componentArtifacts;
1158 public ComponentArtifacts getComponentArtifacts() {
1159 return componentArtifacts;
1163 private class ComponentArtifacts {
1165 //artifacts of the component and CI's artifacts contained in it's composition (represents Informational, Deployment & Resource folders of main component)
1166 private ComponentTypeArtifacts mainTypeAndCIArtifacts;
1167 //artifacts of all component types mapped by their tosca name
1168 private Map<String, ComponentTypeArtifacts> componentTypeArtifacts;
1170 public ComponentArtifacts() {
1171 mainTypeAndCIArtifacts = new ComponentTypeArtifacts();
1172 componentTypeArtifacts = new HashMap<>();
1175 public ComponentTypeArtifacts getMainTypeAndCIArtifacts() {
1176 return mainTypeAndCIArtifacts;
1179 public void setMainTypeAndCIArtifacts(ComponentTypeArtifacts componentInstanceArtifacts) {
1180 this.mainTypeAndCIArtifacts = componentInstanceArtifacts;
1183 public Map<String, ComponentTypeArtifacts> getComponentTypeArtifacts() {
1184 return componentTypeArtifacts;
1189 * The artifacts of the component and of all its composed instances
1191 private class ComponentTypeArtifacts {
1193 private ArtifactsInfo componentArtifacts; //component artifacts (describes the Informational Deployment folders)
1195 private Map<String, ArtifactsInfo> componentInstancesArtifacts; //artifacts of the composed instances mapped by the resourceInstance normalized name (describes the Resources folder)
1197 public ComponentTypeArtifacts() {
1198 componentArtifacts = new ArtifactsInfo();
1199 componentInstancesArtifacts = new HashMap<>();
1202 public ArtifactsInfo getComponentArtifacts() {
1203 return componentArtifacts;
1206 public void setComponentArtifacts(ArtifactsInfo artifactsInfo) {
1207 this.componentArtifacts = artifactsInfo;
1210 public Map<String, ArtifactsInfo> getComponentInstancesArtifacts() {
1211 return componentInstancesArtifacts;
1214 public void addComponentInstancesArtifacts(String normalizedName, ArtifactsInfo artifactsInfo) {
1215 componentInstancesArtifacts.put(normalizedName, artifactsInfo);
1220 * The artifacts Definition saved by their structure
1222 private class ArtifactsInfo {
1223 //Key is the type of artifacts(Informational/Deployment)
1225 //Value is a map between an artifact type and a list of all artifacts of this type
1226 private Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> artifactsInfoField;
1228 public ArtifactsInfo() {
1229 this.artifactsInfoField = new EnumMap<>(ArtifactGroupTypeEnum.class);
1232 public Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> getArtifactsInfo() {
1233 return artifactsInfoField;
1236 public void addArtifactsToGroup(ArtifactGroupTypeEnum artifactGroup, Map<String, List<ArtifactDefinition>> artifactsDefinition) {
1237 if (artifactsInfoField.get(artifactGroup) == null) {
1238 artifactsInfoField.put(artifactGroup, artifactsDefinition);
1240 Map<String, List<ArtifactDefinition>> artifactTypeEnumListMap = artifactsInfoField.get(artifactGroup);
1241 artifactTypeEnumListMap.putAll(artifactsDefinition);
1242 artifactsInfoField.put(artifactGroup, artifactTypeEnumListMap);
1246 public boolean isEmpty() {
1247 return artifactsInfoField.isEmpty();
1250 public boolean isNotEmpty() {
1255 public static class ToscaErrorException extends Exception {
1257 ToscaErrorException(ToscaError error) {
1258 super("Error while exporting component's interface (toscaError:" + error + ")");