2 * ========================LICENSE_START=================================
3 * Copyright (C) 2021 Nordix Foundation. All rights reserved.
4 * ======================================================================
5 * Modifications Copyright (C) 2021 AT&T Intellectual Property. 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===================================
21 package org.onap.policy.clamp.controlloop.participant.kubernetes.helm;
23 import java.io.BufferedReader;
25 import java.io.IOException;
26 import java.io.InputStreamReader;
27 import java.lang.invoke.MethodHandles;
28 import java.nio.charset.StandardCharsets;
29 import java.util.ArrayList;
30 import java.util.List;
32 import org.apache.commons.io.IOUtils;
33 import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException;
34 import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
35 import org.onap.policy.clamp.controlloop.participant.kubernetes.service.ChartStore;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.springframework.beans.factory.annotation.Autowired;
39 import org.springframework.stereotype.Component;
42 * Client to talk with Helm cli. Supports helm3 + version
45 public class HelmClient {
48 private ChartStore chartStore;
50 private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
55 * @param chart name and version.
56 * @throws ServiceException incase of error
58 public void installChart(ChartInfo chart) throws ServiceException {
59 var processBuilder = prepareCreateNamespaceCommand(chart.getNamespace());
61 executeCommand(processBuilder);
62 } catch (ServiceException e) {
63 logger.warn("Namespace not created", e);
65 processBuilder = prepareInstallCommand(chart);
66 logger.info("Installing helm chart {} from the repository {} ", chart.getChartId().getName(),
67 chart.getRepository());
68 executeCommand(processBuilder);
69 logger.info("Chart {} installed successfully", chart.getChartId().getName());
73 * Finds helm chart repository for the chart.
75 * @param chart ChartInfo.
76 * @return the chart repository as a string
77 * @throws ServiceException in case of error
78 * @throws IOException in case of IO errors
80 public String findChartRepository(ChartInfo chart) throws ServiceException, IOException {
82 String repository = verifyConfiguredRepo(chart);
83 if (repository != null) {
86 var localHelmChartDir = chartStore.getAppPath(chart.getChartId()).toString();
87 logger.info("Chart not found in helm repositories, verifying local repo {} ", localHelmChartDir);
88 if (verifyLocalHelmRepo(new File(localHelmChartDir + "/" + chart.getChartId().getName()))) {
89 repository = localHelmChartDir;
96 * Verify helm chart in configured repositories.
97 * @param chart chartInfo
99 * @throws IOException incase of error
100 * @throws ServiceException incase of error
102 public String verifyConfiguredRepo(ChartInfo chart) throws IOException, ServiceException {
103 logger.info("Looking for helm chart {} in all the configured helm repositories", chart.getChartId().getName());
104 String repository = null;
105 var builder = helmRepoVerifyCommand(chart.getChartId().getName());
106 String output = executeCommand(builder);
107 try (var reader = new BufferedReader(new InputStreamReader(IOUtils.toInputStream(output,
108 StandardCharsets.UTF_8)))) {
109 String line = reader.readLine();
110 while (line != null) {
111 if (line.contains(chart.getChartId().getName())) {
112 repository = line.split("/")[0];
113 logger.info("Helm chart located in the repository {} ", repository);
116 line = reader.readLine();
125 * @param chart name and version.
126 * @throws ServiceException incase of error
128 public void uninstallChart(ChartInfo chart) throws ServiceException {
129 executeCommand(prepareUnInstallCommand(chart));
134 * Execute helm cli bash commands .
135 * @param processBuilder processbuilder
136 * @return string output
137 * @throws ServiceException incase of error.
139 public static String executeCommand(ProcessBuilder processBuilder) throws ServiceException {
140 var commandStr = toString(processBuilder);
143 var process = processBuilder.start();
145 int exitValue = process.exitValue();
147 if (exitValue != 0) {
148 var error = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8);
149 if (! error.isEmpty()) {
150 throw new ServiceException("Command execution failed: " + commandStr + " " + error);
154 var output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8);
155 logger.debug("Command <{}> execution, output: {}", commandStr, output);
158 } catch (InterruptedException ie) {
159 Thread.currentThread().interrupt();
160 throw new ServiceException("Failed to execute the Command: " + commandStr + ", the command was interrupted",
162 } catch (Exception exc) {
163 throw new ServiceException("Failed to execute the Command: " + commandStr, exc);
167 private ProcessBuilder prepareInstallCommand(ChartInfo chart) {
170 List<String> helmArguments = new ArrayList<>(
173 "install", chart.getReleaseName(), chart.getRepository() + "/" + chart.getChartId().getName(),
174 "--version", chart.getChartId().getVersion(),
175 "--namespace", chart.getNamespace()
180 // Verify if values.yaml/override parameters available for the chart
181 var localOverrideYaml = chartStore.getOverrideFile(chart);
183 if (verifyLocalHelmRepo(localOverrideYaml)) {
184 logger.info("Override yaml available for the helm chart");
185 helmArguments.addAll(List.of("--values", localOverrideYaml.getPath()));
188 if (chart.getOverrideParams() != null) {
189 for (Map.Entry<String, String> entry : chart.getOverrideParams().entrySet()) {
190 helmArguments.addAll(List.of("--set", entry.getKey() + "=" + entry.getValue()));
193 return new ProcessBuilder().command(helmArguments);
196 private ProcessBuilder prepareUnInstallCommand(ChartInfo chart) {
197 return new ProcessBuilder("helm", "delete", chart.getReleaseName(), "--namespace",
198 chart.getNamespace());
201 private ProcessBuilder prepareCreateNamespaceCommand(String namespace) {
202 return new ProcessBuilder().command("kubectl", "create", "namespace", namespace);
205 private ProcessBuilder helmRepoVerifyCommand(String chartName) {
206 return new ProcessBuilder().command("sh", "-c", "helm search repo | grep " + chartName);
210 private void updateHelmRepo() throws ServiceException {
211 logger.info("Updating local helm repositories before verifying the chart");
212 executeCommand(new ProcessBuilder().command("helm", "repo", "update"));
213 logger.debug("Helm repositories updated successfully");
216 private boolean verifyLocalHelmRepo(File localFile) {
217 return localFile.exists();
220 protected static String toString(ProcessBuilder processBuilder) {
221 return String.join(" ", processBuilder.command());