--- /dev/null
+/*-
+ * ============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();
+ }
+}
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;
@RestController("chartController")
@RequestMapping("helm")
-@Api(tags = {"chart"})
+@Api(tags = {"k8s-participant"})
public class ChartController {
@Autowired
* @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")})
* @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<Object> deleteChart(@PathVariable("name") String name,
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<Object> 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);
+ }
}
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);
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;
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;
* @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.
*
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();
if (verifyLocalHelmRepo(new File(localHelmChartDir + "/" + chart.getChartId().getName()))) {
repository = localHelmChartDir;
}
-
return repository;
}
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;
}
}
}
+ 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<String> 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<String> helmArguments = List.of("sh", "-c", "helm repo ls | grep " + repo.getRepoName());
+ return new ProcessBuilder().command(helmArguments);
+ }
+
+ private ProcessBuilder prepareVerifyNamespaceCommand(String namespace) {
+ List<String> helmArguments = List.of("sh", "-c", "kubectl get ns | grep " + namespace);
+ return new ProcessBuilder().command(helmArguments);
+ }
+
private ProcessBuilder prepareInstallCommand(ChartInfo chart) {
// @formatter:off
List<String> 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
@NonNull
private String namespace;
- private String repository;
+ private HelmRepository repository;
private Map<String, String> overrideParams;
--- /dev/null
+/*-
+ * ========================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;
+}
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;
*/
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.
reportingTimeIntervalMs: 120000
description: Participant Description
participantId:
- name: K8sParticipant0
- version: 1.0.0
+ name: org.onap.k8s.controlloop.K8SControlLoopParticipant
+ version: 2.3.4
participantType:
name: org.onap.k8s.controlloop.K8SControlLoopParticipant
version: 2.3.4
# Configuration of the HTTP/REST server. The parameters are defined and handled by the springboot framework.
# See springboot documentation.
port: 8083
+ servlet:
+ context-path: /onap/k8sparticipant
logging:
# Configuration of logging