9fa3100be78791ed248d403987ea19b51c63c1d4
[so/adapters/so-cnf-adapter.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2023 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  */
20
21 package org.onap.so.cnfm.lcm.bpmn.flows.tasks;
22
23 import static org.onap.so.cnfm.lcm.bpmn.flows.CamundaVariableNameConstants.AS_INSTANCE_ID_PARAM_NAME;
24 import static org.onap.so.cnfm.lcm.bpmn.flows.CamundaVariableNameConstants.DEPLOYMENT_ITEM_INSTANTIATE_REQUESTS;
25 import static org.onap.so.cnfm.lcm.bpmn.flows.CamundaVariableNameConstants.INSTANTIATE_AS_REQUEST_PARAM_NAME;
26 import static org.onap.so.cnfm.lcm.database.beans.JobStatusEnum.FINISHED;
27 import static org.onap.so.cnfm.lcm.database.beans.JobStatusEnum.IN_PROGRESS;
28 import static org.onap.so.cnfm.lcm.database.beans.JobStatusEnum.STARTED;
29
30 import java.io.ByteArrayInputStream;
31 import java.io.ByteArrayOutputStream;
32 import java.io.File;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.io.OutputStream;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 import java.nio.file.Paths;
39 import java.util.HashMap;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.NoSuchElementException;
44 import java.util.Optional;
45 import java.util.Set;
46 import java.util.TreeSet;
47 import java.util.stream.Collectors;
48 import java.util.zip.ZipInputStream;
49 import org.apache.commons.io.FileUtils;
50 import org.apache.logging.log4j.util.Strings;
51 import org.camunda.bpm.engine.delegate.DelegateExecution;
52 import org.onap.so.cnfm.lcm.bpmn.flows.exceptions.KubeConfigFileNotFoundException;
53 import org.onap.so.cnfm.lcm.bpmn.flows.exceptions.SdcPackageRequestFailureException;
54 import org.onap.so.cnfm.lcm.bpmn.flows.extclients.sdc.SdcCsarPackageParser;
55 import org.onap.so.cnfm.lcm.bpmn.flows.extclients.sdc.SdcPackageProvider;
56 import org.onap.so.cnfm.lcm.bpmn.flows.service.KubConfigProvider;
57 import org.onap.so.cnfm.lcm.database.beans.AsDeploymentItem;
58 import org.onap.so.cnfm.lcm.database.beans.AsInst;
59 import org.onap.so.cnfm.lcm.database.beans.AsLifecycleParam;
60 import org.onap.so.cnfm.lcm.database.beans.State;
61 import org.onap.so.cnfm.lcm.database.service.DatabaseServiceProvider;
62 import org.onap.so.cnfm.lcm.model.AsInfoModificationRequestDeploymentItems;
63 import org.onap.so.cnfm.lcm.model.InstantiateAsRequest;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66 import org.springframework.beans.factory.annotation.Autowired;
67 import org.springframework.beans.factory.annotation.Value;
68 import org.springframework.stereotype.Component;
69
70 /**
71  *
72  * @author Waqas Ikram (waqas.ikram@est.tech)
73  *
74  */
75 @Component
76 public class InstantiateAsTask extends AbstractServiceTask {
77     private static final String KUBE_CONFIG_FILE_PARAM_NAME = "kubeConfigFile";
78     private static final String DEPLOY_ITEM_INST_ID_TO_HELM_FILE_MAPPING_PARAM_NAME =
79             "asDeploymentItemInstIdToHelmFileMapping";
80     private static final String IS_AS_INSTANTIATION_SUCCESSFUL_PARAM_NAME = "isAsInstantiationSuccessful";
81
82     private static final Logger logger = LoggerFactory.getLogger(InstantiateAsTask.class);
83
84     private final String csarDir;
85
86     private final SdcPackageProvider sdcPackageProvider;
87
88     private final SdcCsarPackageParser sdcParser;
89     private final KubConfigProvider kubConfigProvider;
90
91     @Autowired
92     public InstantiateAsTask(final DatabaseServiceProvider databaseServiceProvider,
93             final SdcPackageProvider sdcPackageProvider, final SdcCsarPackageParser sdcParser,
94             final KubConfigProvider kubConfigProvider, @Value("${cnfm.csar.dir:/app/csar}") final String csarDir) {
95         super(databaseServiceProvider);
96         this.sdcPackageProvider = sdcPackageProvider;
97         this.sdcParser = sdcParser;
98         this.kubConfigProvider = kubConfigProvider;
99         this.csarDir = csarDir;
100     }
101
102     public void setJobStatusToStarted(final DelegateExecution execution) {
103         setJobStatus(execution, STARTED, "Instantiate AS workflow process started");
104     }
105
106     public void setJobStatusToFinished(final DelegateExecution execution) {
107         setJobStatus(execution, FINISHED, "Instantiate AS workflow process finished");
108     }
109
110     public void updateAsInstanceStatusToInstantiating(final DelegateExecution execution) {
111         logger.info("Executing updateAsInstanceStatusToInstantiating");
112         setJobStatus(execution, IN_PROGRESS, "Updating AsInst Status to " + State.INSTANTIATING);
113         updateAsInstanceStatus(execution, State.INSTANTIATING);
114
115         logger.info("Finished executing updateNsInstanceStatusToInstantiating  ...");
116     }
117
118     public void updateAsInstanceStatusToInstantiated(final DelegateExecution execution) {
119         logger.info("Executing updateAsInstanceStatusToInstantiated");
120
121         final String asInstId = (String) execution.getVariable(AS_INSTANCE_ID_PARAM_NAME);
122         setJobStatus(execution, FINISHED, "Successfully " + State.INSTANTIATED + " AS: " + asInstId);
123
124         updateAsInstanceStatus(execution, State.INSTANTIATED);
125         logger.info("Finished executing updateAsInstanceStatusToInstantiated  ...");
126     }
127
128     public void downloadHelmPackagesFromSdc(final DelegateExecution execution) {
129         logger.info("Executing downloadHelmPackages ...");
130         setJobStatus(execution, IN_PROGRESS, "Downloading helm packages");
131         final String asInstId = (String) execution.getVariable(AS_INSTANCE_ID_PARAM_NAME);
132
133         final AsInst asInst = getAsInst(execution);
134         final Optional<byte[]> optional = getSdcResourcePackage(execution, asInst.getAsdId());
135
136         if (optional.isEmpty()) {
137             final String message = "Unable to find ASD package using asdId: " + asInst.getAsdId();
138             logger.error(message);
139             abortOperation(execution, message);
140         }
141
142         final List<AsDeploymentItem> asDeploymentItems =
143                 databaseServiceProvider.getAsDeploymentItemByAsInstId(asInstId);
144
145         final File dir = mkdirIfnotExists(csarDir, asInstId);
146
147         final Map<String, String> asDeploymentItemInstIdToHelmFileMapping = new HashMap<>();
148
149         asDeploymentItems.forEach(asDeploymentItem -> {
150             try (final ByteArrayInputStream stream = new ByteArrayInputStream(optional.get());
151                     final ZipInputStream zipInputStream = new ZipInputStream(stream);) {
152
153                 final String artifactFilePath = asDeploymentItem.getArtifactFilePath();
154                 final String asDeploymentItemInstId = asDeploymentItem.getAsDeploymentItemInstId();
155                 try (final ByteArrayOutputStream helmByteArrayOutputStream =
156                         sdcParser.getFileInZip(zipInputStream, artifactFilePath);) {
157
158                     final Path artifactPath = Paths.get(artifactFilePath);
159                     final Path path = dir.toPath().resolve(asDeploymentItem.getAsDeploymentItemInstId())
160                             .resolve(artifactPath.getFileName());
161
162                     if (!Files.exists(path.getParent())) {
163                         final File parentDir = path.getParent().toFile();
164                         logger.debug("Creating sub directories to download helm chart file {}", parentDir);
165                         parentDir.mkdirs();
166                     }
167
168                     if (Files.exists(path)) {
169                         logger.debug("{} file already exists will remove it", path);
170                         Files.delete(path);
171                     }
172
173                     try (final OutputStream outputStream = new FileOutputStream(path.toString())) {
174                         helmByteArrayOutputStream.writeTo(outputStream);
175                     }
176
177                     asDeploymentItemInstIdToHelmFileMapping.put(asDeploymentItemInstId, path.toString());
178
179                 }
180             } catch (final IOException ioException) {
181                 final String message = "Unexpected exception occured while processing CSAR " + asInst.getAsdId();
182                 logger.error(message, ioException);
183                 abortOperation(execution, message);
184             } catch (final NoSuchElementException noSuchElementException) {
185                 final String message = "Unable to find artifact " + asDeploymentItem.getArtifactFilePath();
186                 logger.error(message, noSuchElementException);
187                 abortOperation(execution, message);
188             } catch (final Exception exception) {
189                 final String message = "Unexpected exception occured while downloading helm packages";
190                 logger.error(message, exception);
191                 abortOperation(execution, message);
192             }
193         });
194
195         logger.info("asDeploymentItemInstIdToHelmFileMapping: {}", asDeploymentItemInstIdToHelmFileMapping);
196         execution.setVariable(DEPLOY_ITEM_INST_ID_TO_HELM_FILE_MAPPING_PARAM_NAME,
197                 asDeploymentItemInstIdToHelmFileMapping);
198
199         logger.info("Finished executing downloadHelmPackages ...");
200     }
201
202     private Optional<byte[]> getSdcResourcePackage(final DelegateExecution execution, final String asdId) {
203         try {
204             return sdcPackageProvider.getSdcResourcePackage(asdId);
205         } catch (final SdcPackageRequestFailureException exception) {
206             final String message = "Unexpected exception occured while getting asd package using asdId: " + asdId;
207             logger.error(message);
208             abortOperation(execution, message);
209         }
210         return Optional.empty();
211     }
212
213     public void prepareInstantiateDeploymentItemRequests(final DelegateExecution execution) {
214         logger.info("Executing prepareInstantiateDeploymentItemRequests ...");
215         setJobStatus(execution, IN_PROGRESS, "Preparing InstantiateDeploymentItemRequest requests");
216
217         final String asInstId = (String) execution.getVariable(AS_INSTANCE_ID_PARAM_NAME);
218         final InstantiateAsRequest instantiateAsRequest =
219                 (InstantiateAsRequest) execution.getVariable(INSTANTIATE_AS_REQUEST_PARAM_NAME);
220
221         @SuppressWarnings("unchecked")
222         final Map<String, String> asDeploymentItemInstIdToHelmFileMapping =
223                 (Map<String, String>) execution.getVariable(DEPLOY_ITEM_INST_ID_TO_HELM_FILE_MAPPING_PARAM_NAME);
224
225         final String kubeConfigFile = (String) execution.getVariable(KUBE_CONFIG_FILE_PARAM_NAME);
226
227         final List<AsDeploymentItem> asDeploymentItems =
228                 databaseServiceProvider.getAsDeploymentItemByAsInstId(asInstId);
229
230         final Set<InstantiateDeploymentItemRequest> requests = new TreeSet<>();
231         final Map<String, Object> lifeCycleParamMap = instantiateAsRequest.getDeploymentItems().stream()
232                 .collect(Collectors.toMap(AsInfoModificationRequestDeploymentItems::getDeploymentItemsId,
233                         AsInfoModificationRequestDeploymentItems::getLifecycleParameterKeyValues));
234         asDeploymentItems.forEach(asDeploymentItem -> {
235
236             final String asDeploymentItemInstId = asDeploymentItem.getAsDeploymentItemInstId();
237             final String artifactFilePath = asDeploymentItemInstIdToHelmFileMapping.get(asDeploymentItemInstId);
238             final String releaseName = asDeploymentItem.getReleaseName();
239
240             if (Strings.isEmpty(artifactFilePath)) {
241                 final String message =
242                         "Unable to find helm artifact for asDeploymentItemInstId: " + asDeploymentItemInstId;
243                 abortOperation(execution, message);
244             }
245
246
247             @SuppressWarnings("unchecked")
248             final Map<String, String> lifeCycleParams =
249                     (Map<String, String>) lifeCycleParamMap.get(asDeploymentItem.getItemId());
250
251             final List<AsLifecycleParam> requiredParams = asDeploymentItem.getAsLifecycleParams();
252
253             checkForLifecycleParametersAbort(execution, lifeCycleParams, requiredParams);
254             requests.add(new InstantiateDeploymentItemRequest().asInstId(asInstId)
255                     .asDeploymentItemInstId(asDeploymentItemInstId).asDeploymentItemName(asDeploymentItem.getName())
256                     .helmArtifactFilePath(artifactFilePath).deploymentOrder(asDeploymentItem.getDeploymentOrder())
257                     .kubeConfigFile(kubeConfigFile).lifeCycleParameters(lifeCycleParams).releaseName((releaseName)));
258
259         });
260
261         execution.setVariable(DEPLOYMENT_ITEM_INSTANTIATE_REQUESTS, requests);
262
263         logger.info("Finished executing prepareInstantiateDeploymentItemRequests ...");
264
265     }
266
267     private void checkForLifecycleParametersAbort(final DelegateExecution execution,
268             final Map<String, String> lifeCycleParams, final List<AsLifecycleParam> requiredParams) {
269         if (!requiredParams.isEmpty()) {
270             if (isNullOrEmptyMap(lifeCycleParams)) {
271                 abortOnLifecycleParams(execution, "no lifecycle parameters in request");
272             }
273             final Iterator<AsLifecycleParam> it = requiredParams.iterator();
274             while (it.hasNext()) {
275                 final String next = it.next().getLifecycleParam();
276                 if (!lifeCycleParams.containsKey(next)) {
277                     abortOnLifecycleParams(execution, "parameter missing: " + next);
278                 }
279             }
280         }
281     }
282
283     private void abortOnLifecycleParams(final DelegateExecution execution, final String reason) {
284         final String message = "Lifecycle parameter error, " + reason;
285         abortOperation(execution, message);
286     }
287
288     public void checkIfDeploymentItemsInstantiationWasSuccessful(final DelegateExecution execution) {
289         logger.info("Executing checkIfDeploymentItemsInstantiationWasSuccessful");
290
291         @SuppressWarnings("unchecked")
292         final Set<InstantiateDeploymentItemRequest> requests =
293                 (Set<InstantiateDeploymentItemRequest>) execution.getVariable(DEPLOYMENT_ITEM_INSTANTIATE_REQUESTS);
294
295         final String asInstId = (String) execution.getVariable(AS_INSTANCE_ID_PARAM_NAME);
296         final List<AsDeploymentItem> asDeploymentItems =
297                 databaseServiceProvider.getAsDeploymentItemByAsInstId(asInstId);
298
299
300         if (asDeploymentItems == null || asDeploymentItems.isEmpty()) {
301             final String message = "Found empty asDeploymentItems";
302             abortOperation(execution, message);
303         }
304
305         if (requests.size() != asDeploymentItems.size()) {
306             final String message = "Missing asDeploymentItems. Request triggered has: " + requests.size()
307                     + " asDeploymentItems but database has: " + asDeploymentItems.size();
308             abortOperation(execution, message);
309         }
310
311         execution.setVariable(IS_AS_INSTANTIATION_SUCCESSFUL_PARAM_NAME, true);
312
313         asDeploymentItems.stream().forEach(asDeploymentItem -> {
314             logger.info("Checking if AsDeploymentItem {} was successfull Status: {}",
315                     asDeploymentItem.getAsDeploymentItemInstId(), asDeploymentItem.getStatus());
316             if (!State.INSTANTIATED.equals(asDeploymentItem.getStatus())) {
317                 logger.error("AsDeploymentItem : {} {} instantiation failed",
318                         asDeploymentItem.getAsDeploymentItemInstId(), asDeploymentItem.getName());
319                 execution.setVariable(IS_AS_INSTANTIATION_SUCCESSFUL_PARAM_NAME, false);
320             } else {
321                 cleanUpDeploymentItemDirectory(asInstId, asDeploymentItem.getAsDeploymentItemInstId());
322             }
323         });
324
325         cleanUpInstanceIdDirectory(asInstId);
326         logger.info("Finished executing checkIfDeploymentItemsInstantiationWasSuccessful  ...");
327     }
328
329     private void cleanUpDeploymentItemDirectory(final String asInstId, final String deploymentItemInstId) {
330         logger.info("Executing Cleaning up Deployment Item Directory {}", deploymentItemInstId);
331         final Path helmChartDirPath = Paths.get(csarDir, asInstId).resolve(deploymentItemInstId);
332         if (Files.exists(helmChartDirPath)) {
333             logger.debug("Will clean up the directory {}", helmChartDirPath);
334             try {
335                 FileUtils.deleteDirectory(helmChartDirPath.toFile());
336             } catch (final IOException e) {
337                 logger.debug("Error deleting the directory {}", helmChartDirPath);
338             }
339         }
340     }
341
342     private void cleanUpInstanceIdDirectory(final String asInstId) {
343         logger.debug("Executing Cleaning up Instance Id Directory {}", asInstId);
344         final Path dirPath = Paths.get(csarDir, asInstId);
345         if (Files.exists(dirPath) && (dirPath.toFile().list().length == 0)) {
346             logger.debug("Will clean up the instance id directory {}", dirPath);
347             try {
348                 Files.delete(dirPath);
349             } catch (final IOException e) {
350                 logger.debug("Error deleting the instance id directory {}", dirPath);
351             }
352         } else {
353             logger.debug("Will not clean up the instance id directory. {} is not Empty", dirPath);
354         }
355     }
356
357     public void checkifKubConfigFileAvailable(final DelegateExecution execution) {
358         logger.info("Executing checkifKubConfigFileAvailable");
359         try {
360             final AsInst asInst = getAsInst(execution);
361
362             final Path kubeConfigFile = kubConfigProvider.getKubeConfigFile(asInst.getCloudOwner(),
363                     asInst.getCloudRegion(), asInst.getTenantId());
364
365             execution.setVariable(KUBE_CONFIG_FILE_PARAM_NAME, kubeConfigFile.toString());
366
367         } catch (final KubeConfigFileNotFoundException exception) {
368             final String message = "Unable to find kube-config file on filesystem";
369             logger.error(message, exception);
370             abortOperation(execution, message);
371
372         }
373
374         logger.info("Finished executing checkifKubConfigFileAvailable  ...");
375
376     }
377
378     public void logTimeOut(final DelegateExecution execution) {
379         logger.error("Deployment items instantiation timedOut ...");
380         final String asInstId = (String) execution.getVariable(AS_INSTANCE_ID_PARAM_NAME);
381         final List<AsDeploymentItem> asDeploymentItems =
382                 databaseServiceProvider.getAsDeploymentItemByAsInstId(asInstId);
383         if (asDeploymentItems != null) {
384             asDeploymentItems.stream()
385                     .forEach(asDeploymentItem -> logger.info("Current status {} of asDeploymentItem: {}",
386                             asDeploymentItem.getStatus(), asDeploymentItem.getName()));
387         }
388     }
389
390     public void setJobStatusToError(final DelegateExecution execution) {
391         setJobStatusToError(execution, "Instantiate AS workflow process failed");
392     }
393
394     private File mkdirIfnotExists(final String parentDir, final String dirname) {
395         final Path dirPath = Paths.get(parentDir, dirname);
396         final File dir = dirPath.toFile();
397         if (!dir.exists()) {
398             logger.debug("Creating directory to download helm chart file {}", dir);
399             dir.mkdir();
400         }
401         return dir;
402     }
403
404     public static boolean isNullOrEmptyMap(final Map<?, ?> map) {
405         return (map == null || map.isEmpty());
406     }
407 }