From 02b3cbfb009ad9fdfc9112073742c8d30dfc11e9 Mon Sep 17 00:00:00 2001 From: rameshiyer27 Date: Mon, 23 Aug 2021 16:34:05 +0100 Subject: Add support for configuring new helm repository Issue-ID: POLICY-3480 Signed-off-by: zrrmmua Change-Id: I2499cd8ab9a4cf6390c2c4d834264b3754855d23 --- .../kubernetes/configurations/SpringFoxConfig.java | 45 +++++++++++ .../kubernetes/controller/ChartController.java | 32 +++++++- .../handler/ControlLoopElementHandler.java | 4 - .../participant/kubernetes/helm/HelmClient.java | 90 ++++++++++++++++------ .../participant/kubernetes/models/ChartInfo.java | 2 +- .../kubernetes/models/HelmRepository.java | 39 ++++++++++ .../kubernetes/service/ChartService.java | 23 +++++- 7 files changed, 200 insertions(+), 35 deletions(-) create mode 100644 participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/SpringFoxConfig.java create mode 100644 participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/HelmRepository.java (limited to 'participant/participant-impl/participant-impl-kubernetes/src/main/java') diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/SpringFoxConfig.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/SpringFoxConfig.java new file mode 100644 index 000000000..09a497705 --- /dev/null +++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/SpringFoxConfig.java @@ -0,0 +1,45 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.policy.clamp.controlloop.participant.kubernetes.configurations; + +import org.onap.policy.clamp.controlloop.participant.kubernetes.controller.ChartController; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +@Configuration +public class SpringFoxConfig { + + /** + * Docket Spring Fox Config. + * + * @return Docket + */ + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2).select() + .apis(RequestHandlerSelectors.basePackage(ChartController.class.getPackageName())) + .paths(PathSelectors.any()).build(); + } +} diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/controller/ChartController.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/controller/ChartController.java index 23605e641..d041300a8 100644 --- a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/controller/ChartController.java +++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/controller/ChartController.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException; import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo; import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartList; +import org.onap.policy.clamp.controlloop.participant.kubernetes.models.HelmRepository; import org.onap.policy.clamp.controlloop.participant.kubernetes.models.InstallationInfo; import org.onap.policy.clamp.controlloop.participant.kubernetes.service.ChartService; import org.onap.policy.common.utils.coder.CoderException; @@ -48,7 +49,7 @@ import org.springframework.web.multipart.MultipartFile; @RestController("chartController") @RequestMapping("helm") -@Api(tags = {"chart"}) +@Api(tags = {"k8s-participant"}) public class ChartController { @Autowired @@ -124,7 +125,7 @@ public class ChartController { * @throws ServiceException in case of error * @throws IOException in case of IO error */ - @PostMapping(path = "/charts", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, + @PostMapping(path = "/onboard/chart", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ApiOperation(value = "Onboard the Chart") @ApiResponses(value = {@ApiResponse(code = 201, message = "Chart Onboarded")}) @@ -150,7 +151,7 @@ public class ChartController { * @param version version of the chart * @return Status of operation */ - @DeleteMapping(path = "/charts/{name}/{version}") + @DeleteMapping(path = "/chart/{name}/{version}") @ApiOperation(value = "Delete the chart") @ApiResponses(value = {@ApiResponse(code = 204, message = "Chart Deleted")}) public ResponseEntity deleteChart(@PathVariable("name") String name, @@ -164,4 +165,29 @@ public class ChartController { chartService.deleteChart(chart); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } + + /** + * REST endpoint to configure a helm Repository. + * + * @param repo Helm repository to be configured + * @return Status of the operation + * @throws ServiceException in case of error + * @throws IOException in case of IO error + */ + @PostMapping(path = "/repo", consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + @ApiOperation(value = "Configure helm repository") + @ApiResponses(value = {@ApiResponse(code = 201, message = "Repository added")}) + public ResponseEntity configureRepo(@RequestBody String repo) + throws ServiceException, IOException { + HelmRepository repository; + try { + repository = CODER.decode(repo, HelmRepository.class); + } catch (CoderException e) { + throw new ServiceException("Error parsing the repository information", e); + } + chartService.configureRepository(repository); + + return new ResponseEntity<>(HttpStatus.CREATED); + } } diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/handler/ControlLoopElementHandler.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/handler/ControlLoopElementHandler.java index 3f59c0822..8aa74f30f 100644 --- a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/handler/ControlLoopElementHandler.java +++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/handler/ControlLoopElementHandler.java @@ -134,10 +134,6 @@ public class ControlLoopElementHandler implements ControlLoopElementListener { LOGGER.info("Installation request received for the Helm Chart {} ", chartData); try { var chartInfo = CODER.convert(chartData, ChartInfo.class); - var repositoryValue = chartData.get("repository"); - if (repositoryValue != null) { - chartInfo.setRepository(repositoryValue.toString()); - } chartService.installChart(chartInfo); chartMap.put(element.getId(), chartInfo); diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/helm/HelmClient.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/helm/HelmClient.java index 6a1b98654..7954dbbb9 100644 --- a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/helm/HelmClient.java +++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/helm/HelmClient.java @@ -20,10 +20,8 @@ package org.onap.policy.clamp.controlloop.participant.kubernetes.helm; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -32,6 +30,7 @@ import java.util.Map; import org.apache.commons.io.IOUtils; import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException; import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo; +import org.onap.policy.clamp.controlloop.participant.kubernetes.models.HelmRepository; import org.onap.policy.clamp.controlloop.participant.kubernetes.service.ChartStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,19 +55,34 @@ public class HelmClient { * @throws ServiceException incase of error */ public void installChart(ChartInfo chart) throws ServiceException { - var processBuilder = prepareCreateNamespaceCommand(chart.getNamespace()); - try { + if (! checkNamespaceExists(chart.getNamespace())) { + var processBuilder = prepareCreateNamespaceCommand(chart.getNamespace()); executeCommand(processBuilder); - } catch (ServiceException e) { - logger.warn("Namespace not created", e); } - processBuilder = prepareInstallCommand(chart); + var processBuilder = prepareInstallCommand(chart); logger.info("Installing helm chart {} from the repository {} ", chart.getChartId().getName(), - chart.getRepository()); + chart.getRepository().getRepoName()); executeCommand(processBuilder); logger.info("Chart {} installed successfully", chart.getChartId().getName()); } + /** + * Add repository if doesn't exist. + * @param repo HelmRepository + * @throws ServiceException incase of error + */ + public void addRepository(HelmRepository repo) throws ServiceException { + String output = executeCommand(prepareVerifyRepoCommand(repo)); + if (output.isEmpty()) { + logger.info("Adding repository to helm client"); + executeCommand(prepareRepoAddCommand(repo)); + logger.debug("Added repository {} to the helm client", repo.getRepoName()); + } else { + logger.info("Repository already exists"); + } + } + + /** * Finds helm chart repository for the chart. * @@ -81,6 +95,7 @@ public class HelmClient { updateHelmRepo(); String repository = verifyConfiguredRepo(chart); if (repository != null) { + logger.info("Helm chart located in the repository {} ", repository); return repository; } var localHelmChartDir = chartStore.getAppPath(chart.getChartId()).toString(); @@ -88,7 +103,6 @@ public class HelmClient { if (verifyLocalHelmRepo(new File(localHelmChartDir + "/" + chart.getChartId().getName()))) { repository = localHelmChartDir; } - return repository; } @@ -104,18 +118,7 @@ public class HelmClient { String repository = null; var builder = helmRepoVerifyCommand(chart.getChartId().getName()); String output = executeCommand(builder); - try (var reader = new BufferedReader(new InputStreamReader(IOUtils.toInputStream(output, - StandardCharsets.UTF_8)))) { - String line = reader.readLine(); - while (line != null) { - if (line.contains(chart.getChartId().getName())) { - repository = line.split("/")[0]; - logger.info("Helm chart located in the repository {} ", repository); - return repository; - } - line = reader.readLine(); - } - } + repository = verifyOutput(output, chart.getChartId().getName()); return repository; } @@ -164,17 +167,56 @@ public class HelmClient { } } + private boolean checkNamespaceExists(String namespace) throws ServiceException { + logger.info("Check if namespace {} exists on the cluster", namespace); + String output = executeCommand(prepareVerifyNamespaceCommand(namespace)); + return !output.isEmpty(); + } + + private String verifyOutput(String output, String value) { + for (var line: output.split("\\R")) { + if (line.contains(value)) { + return line.split("/")[0]; + } + } + return null; + } + + private ProcessBuilder prepareRepoAddCommand(HelmRepository repo) { + // @formatter:off + List helmArguments = new ArrayList<>( + List.of( + "helm", + "repo", + "add", repo.getRepoName(), repo.getProtocol() + "://" + repo.getAddress() + ":" + repo.getPort() + )); + if (repo.getUserName() != null && repo.getPassword() != null) { + helmArguments.addAll(List.of("--username", repo.getUserName(), "--password", repo.getPassword())); + } + return new ProcessBuilder().command(helmArguments); + } + + private ProcessBuilder prepareVerifyRepoCommand(HelmRepository repo) { + List helmArguments = List.of("sh", "-c", "helm repo ls | grep " + repo.getRepoName()); + return new ProcessBuilder().command(helmArguments); + } + + private ProcessBuilder prepareVerifyNamespaceCommand(String namespace) { + List helmArguments = List.of("sh", "-c", "kubectl get ns | grep " + namespace); + return new ProcessBuilder().command(helmArguments); + } + private ProcessBuilder prepareInstallCommand(ChartInfo chart) { // @formatter:off List helmArguments = new ArrayList<>( List.of( "helm", - "install", chart.getReleaseName(), chart.getRepository() + "/" + chart.getChartId().getName(), + "install", chart.getReleaseName(), chart.getRepository().getRepoName() + "/" + + chart.getChartId().getName(), "--version", chart.getChartId().getVersion(), "--namespace", chart.getNamespace() - ) - ); + )); // @formatter:on // Verify if values.yaml/override parameters available for the chart diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartInfo.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartInfo.java index b53f2075a..5cbc203ec 100644 --- a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartInfo.java +++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartInfo.java @@ -37,7 +37,7 @@ public class ChartInfo { @NonNull private String namespace; - private String repository; + private HelmRepository repository; private Map overrideParams; diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/HelmRepository.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/HelmRepository.java new file mode 100644 index 000000000..a495c7b5c --- /dev/null +++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/HelmRepository.java @@ -0,0 +1,39 @@ +/*- + * ========================LICENSE_START================================= + * Copyright (C) 2021 Nordix Foundation. All rights reserved. + * ====================================================================== + * 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. + * ========================LICENSE_END=================================== + */ + +package org.onap.policy.clamp.controlloop.participant.kubernetes.models; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class HelmRepository { + + private String repoName; + + private String protocol; + + private String address; + + private String port; + + private String userName; + + private String password; +} diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartService.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartService.java index a1522188d..770bbb291 100644 --- a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartService.java +++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartService.java @@ -24,6 +24,7 @@ import java.util.Collection; import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException; import org.onap.policy.clamp.controlloop.participant.kubernetes.helm.HelmClient; import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo; +import org.onap.policy.clamp.controlloop.participant.kubernetes.models.HelmRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -88,18 +89,34 @@ public class ChartService { */ public void installChart(ChartInfo chart) throws ServiceException, IOException { if (chart.getRepository() == null) { - String repository = findChartRepo(chart); - if (repository == null) { + String repoName = findChartRepo(chart); + if (repoName == null) { logger.error("Chart repository could not be found. Skipping chart Installation " + "for the chart {} ", chart.getChartId().getName()); return; } else { - chart.setRepository(repository); + HelmRepository repo = HelmRepository.builder().repoName(repoName).build(); + chart.setRepository(repo); } + } else { + // Add remote repository if passed via TOSCA + configureRepository(chart.getRepository()); } helmClient.installChart(chart); } + + /** + * Configure remote repository. + * @param repo HelmRepository + * @throws ServiceException incase of error + */ + public void configureRepository(HelmRepository repo) throws ServiceException { + if (repo.getAddress() != null && repo.getPort() != null) { + helmClient.addRepository(repo); + } + } + /** * Finds helm chart repository for a given chart. * @param chart chartInfo. -- cgit 1.2.3-korg