Helm charts values customization using files

Issue-ID: SO-4030
Signed-off-by: nadeeshani.jayathilake <nadeeshani.jayathilake@est.tech>
Change-Id: I1fea315e3f39179161a32762ca53763088426520
diff --git a/readme.md b/readme.md
index 93a5fce..65af3d9 100644
--- a/readme.md
+++ b/readme.md
@@ -15,3 +15,13 @@
 
 Subscribe and post messages with SO tag in onap-discuss group at https://lists.onap.org/g/onap-discuss
 
+# Project Structure
+
+SO-CNFM is composed of a main pom.xml which is structured to build packages (docker image preparation file) and so-cnfm-lcm modules. 
+Also, so-cnfm-lcm is structured to build all submodules and there is individual pom.xml for each submodule.
+so-cnfm-lcm is composed of
+    so-cnfm-lcm-api – generates the POJOs for REST APIs.
+    so-cnfm-lcm-application – creates Spring Boot application jar file.
+    so-cnfm-lcm-bpmn-flows - Camunda BPM engine code flow for step-by-step order flow execution.
+    so-cnfm-lcm-database-service – mariadb for storing metadata.
+    so-cnfm-lcm-service – contains the REST APIs.
diff --git a/so-cnfm-lcm/so-cnfm-lcm-bpmn-flows/src/main/java/org/onap/so/cnfm/lcm/bpmn/flows/extclients/helm/HelmClientImpl.java b/so-cnfm-lcm/so-cnfm-lcm-bpmn-flows/src/main/java/org/onap/so/cnfm/lcm/bpmn/flows/extclients/helm/HelmClientImpl.java
index 7eaf4af..7fa59d5 100644
--- a/so-cnfm-lcm/so-cnfm-lcm-bpmn-flows/src/main/java/org/onap/so/cnfm/lcm/bpmn/flows/extclients/helm/HelmClientImpl.java
+++ b/so-cnfm-lcm/so-cnfm-lcm-bpmn-flows/src/main/java/org/onap/so/cnfm/lcm/bpmn/flows/extclients/helm/HelmClientImpl.java
@@ -27,7 +27,10 @@
 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_REPLICA_SET;
 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_SERVICE;
 import static org.onap.so.cnfm.lcm.bpmn.flows.Constants.KIND_STATEFUL_SET;
+import java.io.IOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -35,8 +38,10 @@
 import java.util.Set;
 import org.jvnet.jaxb2_commons.lang.StringUtils;
 import org.onap.so.cnfm.lcm.bpmn.flows.exceptions.HelmClientExecuteException;
+import org.onap.so.cnfm.lcm.bpmn.flows.service.PropertiesToYamlConverter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -44,6 +49,12 @@
     private static final String KIND_KEY = "kind: ";
     private static final String ANY_UNICODE_NEWLINE = "\\R";
     private static final Logger logger = LoggerFactory.getLogger(HelmClientImpl.class);
+    private final PropertiesToYamlConverter propertiesToYamlConverter;
+
+    @Autowired
+    public HelmClientImpl(final PropertiesToYamlConverter propertiesToYamlConverter) {
+        this.propertiesToYamlConverter = propertiesToYamlConverter;
+    }
 
     private static final Set<String> SUPPORTED_KINDS = Set.of(KIND_JOB, KIND_POD, KIND_SERVICE, KIND_DEPLOYMENT,
             KIND_REPLICA_SET, KIND_DAEMON_SET, KIND_STATEFUL_SET);
@@ -156,17 +167,27 @@
 
     private ProcessBuilder prepareInstallCommand(final String releaseName, final Path kubeconfig, final Path helmChart,
             final Map<String, String> lifeCycleParams) {
-        final List<String> helmArguments = new ArrayList<String>(List.of("helm", "install", releaseName, "-n",
+        final List<String> commands = new ArrayList<String>(List.of("helm", "install", releaseName, "-n",
                 "default", helmChart.toString(), "--kubeconfig", kubeconfig.toString()));
 
         if (lifeCycleParams != null && !lifeCycleParams.isEmpty()) {
-            for (final Map.Entry<String, String> param : lifeCycleParams.entrySet()) {
-                helmArguments
-                        .addAll(List.of("--set", param.getKey().concat("=").concat(String.valueOf(param.getValue()))));
-            }
+            final String fileName = helmChart.getParent().resolve("values.yaml").toString();
+            createYamlFile(fileName, lifeCycleParams);
+            commands.add("-f ".concat(fileName));
         }
+        final List<String> helmArguments = List.of("sh", "-c", toString(commands));
         return new ProcessBuilder().command(helmArguments);
+    }
 
+    private void createYamlFile(final String fileName, final Map<String, String> lifeCycleParams){
+        logger.debug("Will create the runtime values.yaml file.");
+        String yamlContent = propertiesToYamlConverter.getValuesYamlFileContent(lifeCycleParams);
+        try {
+            Files.write(Paths.get(fileName), yamlContent.getBytes());
+        } catch (IOException e) {
+            logger.error("Failed to create the run time life cycle yaml file: {} " + e.getMessage());
+            throw new HelmClientExecuteException("Failed to create the run time life cycle yaml file: {} " + e.getMessage());
+        }
     }
 
     private ProcessBuilder prepareUnInstallCommand(final String releaseName, final Path kubeConfig) {
diff --git a/so-cnfm-lcm/so-cnfm-lcm-bpmn-flows/src/main/java/org/onap/so/cnfm/lcm/bpmn/flows/service/PropertiesToYamlConverter.java b/so-cnfm-lcm/so-cnfm-lcm-bpmn-flows/src/main/java/org/onap/so/cnfm/lcm/bpmn/flows/service/PropertiesToYamlConverter.java
new file mode 100644
index 0000000..07e16c2
--- /dev/null
+++ b/so-cnfm-lcm/so-cnfm-lcm-bpmn-flows/src/main/java/org/onap/so/cnfm/lcm/bpmn/flows/service/PropertiesToYamlConverter.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.cnfm.lcm.bpmn.flows.service;
+import java.util.Map;
+import java.util.TreeMap;
+import org.springframework.stereotype.Service;
+import org.yaml.snakeyaml.Yaml;
+/**
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ *
+ */
+
+@Service
+public class PropertiesToYamlConverter {
+    public String getValuesYamlFileContent(final Map<String, String> lifeCycleParams) {
+        final Map<String, Object> root = new TreeMap<>();
+        lifeCycleParams.entrySet().stream().forEach(entry -> processProperty(root, entry.getKey(), entry.getValue()));
+        final Yaml yaml = new Yaml();
+        return yaml.dumpAsMap(root);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void processProperty(final Map<String, Object> root, final String key, final String value) {
+        Map<String, Object> local = root;
+        final String[] keys = key.split("\\.");
+        final int lastIndex = keys.length - 1;
+        for (int index = 0; index < lastIndex; index++) {
+            final String currentKey = keys[index];
+            if (!local.containsKey(currentKey)) {
+                final Map<String, Object> subMap = new TreeMap<>();
+                local.put(currentKey, subMap);
+                local = subMap;
+                continue;
+            } else {
+                local = (Map<String, Object>) local.get(currentKey);
+            }
+        }
+        local.put(keys[lastIndex], value);
+    }
+}