2 * ========================LICENSE_START=================================
3 * Copyright (C) 2021 Nordix Foundation. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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 * ========================LICENSE_END===================================
19 package org.onap.policy.clamp.controlloop.participant.kubernetes.helm;
21 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.lang.invoke.MethodHandles;
25 import java.nio.charset.StandardCharsets;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
29 import org.apache.commons.io.IOUtils;
30 import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException;
31 import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
32 import org.onap.policy.clamp.controlloop.participant.kubernetes.service.ChartStore;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35 import org.springframework.beans.factory.annotation.Autowired;
36 import org.springframework.stereotype.Component;
39 * Client to talk with Helm cli. Supports helm3 + version
42 public class HelmClient {
45 private ChartStore chartStore;
47 private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
53 * @param chart name and version.
54 * @throws ServiceException incase of error
56 public void installChart(ChartInfo chart) throws ServiceException {
57 var processBuilder = prepareCreateNamespaceCommand(chart.getNamespace());
59 executeCommand(processBuilder);
60 } catch (ServiceException e) {
61 logger.warn("Namespace not created", e);
63 processBuilder = prepareInstallCommand(chart);
64 logger.info("Installing helm chart {} from the repository {} ", chart.getChartName(), chart.getRepository());
65 executeCommand(processBuilder);
66 logger.info("Chart {} installed successfully", chart.getChartName());
70 * Finds helm chart repository for the chart.
72 * @param chart ChartInfo.
73 * @throws ServiceException incase of error
75 public String findChartRepository(ChartInfo chart) throws ServiceException, IOException {
77 logger.info("Looking for helm chart {} in all the configured helm repositories", chart.getChartName());
78 String repository = null;
80 var process = helmRepoVerifyCommand(chart.getChartName()).start();
82 try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
83 String line = reader.readLine();
84 while (line != null) {
85 if (line.contains(chart.getChartName())) {
86 repository = line.split("/")[0];
87 logger.info("Helm chart located in the repository {} ", repository);
90 line = reader.readLine();
94 var localHelmChartDir = chartStore.getAppPath(chart.getChartName(), chart.getVersion()).toString();
95 logger.info("Chart not found in helm repositories, verifying local repo {} ", localHelmChartDir);
96 if (verifyLocalHelmRepo(localHelmChartDir + "/" + chart.getChartName())) {
97 repository = localHelmChartDir;
106 * @param chart name and version.
107 * @throws ServiceException incase of error
109 public void uninstallChart(ChartInfo chart) throws ServiceException {
110 executeCommand(prepareUnInstallCommand(chart));
113 static String executeCommand(ProcessBuilder processBuilder) throws ServiceException {
114 var commandStr = toString(processBuilder);
116 processBuilder.redirectInput(ProcessBuilder.Redirect.DISCARD);
119 var process = processBuilder.start();
121 int exitValue = process.exitValue();
123 if (exitValue != 0) {
124 var error = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8);
125 throw new ServiceException("Command execution failed: " + commandStr + " " + error);
127 var output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8);
128 logger.debug("Command <{}> execution, output: {}", commandStr, output);
130 } catch (InterruptedException ie) {
131 Thread.currentThread().interrupt();
132 throw new ServiceException(
133 "Failed to execute the Command: " + commandStr + ", the command was interrupted", ie);
134 } catch (Exception exc) {
135 throw new ServiceException("Failed to execute the Command: " + commandStr, exc);
139 private ProcessBuilder prepareInstallCommand(ChartInfo chart) {
142 List<String> helmArguments = new ArrayList<>(
145 "install", chart.getReleaseName(), chart.getRepository() + "/" + chart.getChartName(),
146 "--version", chart.getVersion(),
147 "--namespace", chart.getNamespace()
152 // Verify if values.yaml available for the chart
153 var overrideFile = chartStore.getOverrideFile(chart).getPath();
154 if (verifyLocalHelmRepo(overrideFile)) {
155 logger.info("Override yaml file available for the helm chart");
156 helmArguments.addAll(Arrays.asList("--values", overrideFile));
159 return new ProcessBuilder().command(helmArguments);
162 private ProcessBuilder prepareUnInstallCommand(ChartInfo chart) {
163 return new ProcessBuilder("helm", "delete", chart.getReleaseName(), "--namespace", chart.getNamespace());
166 private ProcessBuilder prepareCreateNamespaceCommand(String namespace) {
167 return new ProcessBuilder().command("kubectl", "create", "namespace", namespace);
170 private ProcessBuilder helmRepoVerifyCommand(String chartName) {
171 return new ProcessBuilder().command("bash", "-c", "helm search repo | grep " + chartName);
174 private ProcessBuilder localRepoVerifyCommand(String localFile) {
175 return new ProcessBuilder().command("bash", "-c", "ls " + localFile);
178 private void updateHelmRepo() throws ServiceException {
179 logger.info("Updating local helm repositories before verifying the chart");
180 List<String> helmArguments = Arrays.asList("helm", "repo", "update");
182 executeCommand(new ProcessBuilder().command(helmArguments));
183 logger.debug("Helm repositories updated successfully");
186 private boolean verifyLocalHelmRepo(String localFile) {
187 var isVerified = false;
188 var processBuilder = localRepoVerifyCommand(localFile);
190 executeCommand(processBuilder);
192 } catch (ServiceException e) {
193 logger.error("Unable to verify file in local repository", e);
198 protected static String toString(ProcessBuilder processBuilder) {
199 return String.join(" ", processBuilder.command());