aboutsummaryrefslogtreecommitdiffstats
path: root/participant/participant-impl/participant-impl-kubernetes/src
diff options
context:
space:
mode:
authorliamfallon <liam.fallon@est.tech>2021-06-07 16:41:12 +0100
committerliamfallon <liam.fallon@est.tech>2021-06-09 14:34:20 +0100
commit030ba1a000fc78e4c8778be469a41b14538a58ea (patch)
tree234ef346f04a282628d25fb85647e75a93530fc2 /participant/participant-impl/participant-impl-kubernetes/src
parent20c7487f77d0728d270b2bed34c2c798d17cc12d (diff)
Initial commit for kubernetes participant in CLAMP
Spring application that exposes REST end points for installing, uninstalling, onboarding and deleting of helm charts to/from local directory. CL runtime can also trigger installation and uninstallation of helm charts from both local and configured helm repositories. Junits will be committed as a separate review. Issue-ID: POLICY-3240 Change-Id: I7633b6fd6ad41fc8fa55d3722e44f1b2ec132e50 Signed-off-by: zrrmmua <ramesh.murugan.iyer@est.tech> Signed-off-by: liamfallon <liam.fallon@est.tech>
Diffstat (limited to 'participant/participant-impl/participant-impl-kubernetes/src')
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/Application.java40
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/BeanFactory.java71
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/ParametersConfig.java41
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/ParticipantIntermediaryConfig.java48
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/controller/ChartController.java166
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/exception/ServiceException.java32
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/handler/ControlLoopElementHandler.java147
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/helm/HelmClient.java201
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartInfo.java45
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartList.java31
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/InstallationInfo.java29
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/parameters/ParticipantK8sParameterHandler.java73
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/parameters/ParticipantK8sParameters.java59
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartService.java122
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartStore.java215
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/resources/config/KubernetesParticipantConfig.json56
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/main/resources/config/application.yaml25
-rw-r--r--participant/participant-impl/participant-impl-kubernetes/src/test/resources/KubernetesHelm.yaml154
18 files changed, 1555 insertions, 0 deletions
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/Application.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/Application.java
new file mode 100644
index 000000000..ffa0bceb9
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/Application.java
@@ -0,0 +1,40 @@
+/*-
+ * ============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;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Starter.
+ *
+ */
+@SpringBootApplication
+public class Application {
+ /**
+ * Main class.
+ * @param args args
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
+
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/BeanFactory.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/BeanFactory.java
new file mode 100644
index 000000000..3199d0cd9
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/BeanFactory.java
@@ -0,0 +1,71 @@
+/*-
+ * ========================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.configurations;
+
+import org.apache.catalina.connector.Connector;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.multipart.MultipartResolver;
+import org.springframework.web.multipart.commons.CommonsMultipartResolver;
+
+/**
+ * Bean Factory class for helm client.
+ */
+@Configuration
+public class BeanFactory {
+
+ @Value("${server.http-port}")
+ private int httpPort = 0;
+
+ /**
+ * Method to create servlet container bean.
+ * @return webserver factory
+ */
+ @Bean
+ public ServletWebServerFactory servletContainer() {
+ var tomcat = new TomcatServletWebServerFactory();
+ if (httpPort > 0) {
+ tomcat.addAdditionalTomcatConnectors(getHttpConnector(httpPort));
+ }
+ return tomcat;
+ }
+
+ private static Connector getHttpConnector(int httpPort) {
+ var connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
+ connector.setScheme("http");
+ connector.setPort(httpPort);
+ connector.setSecure(false);
+ return connector;
+ }
+
+ /**
+ * Method to create multipartResolver bean.
+ * @return MultipartResolver
+ */
+ @Bean(name = "multipartResolver")
+ public MultipartResolver multipartResolver() {
+ var multipartResolver = new CommonsMultipartResolver();
+ multipartResolver.setMaxUploadSize(100000);
+ return multipartResolver;
+ }
+
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/ParametersConfig.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/ParametersConfig.java
new file mode 100644
index 000000000..5f2a4e4ad
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/ParametersConfig.java
@@ -0,0 +1,41 @@
+/*-
+ * ============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.common.exception.ControlLoopException;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.parameters.ParticipantK8sParameterHandler;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.parameters.ParticipantK8sParameters;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ParametersConfig {
+
+ @Value("${participant.file}")
+ private String file;
+
+ @Bean
+ public ParticipantK8sParameters participantK8sParameters() throws ControlLoopException {
+ return new ParticipantK8sParameterHandler().toParticipantK8sParameters(file);
+ }
+}
+
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/ParticipantIntermediaryConfig.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/ParticipantIntermediaryConfig.java
new file mode 100644
index 000000000..d8c39925b
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/ParticipantIntermediaryConfig.java
@@ -0,0 +1,48 @@
+/*-
+ * ============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.intermediary.api.ParticipantIntermediaryApi;
+import org.onap.policy.clamp.controlloop.participant.intermediary.api.ParticipantIntermediaryFactory;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.handler.ControlLoopElementHandler;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.parameters.ParticipantK8sParameters;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ParticipantIntermediaryConfig {
+
+ /**
+ * Create ParticipantIntermediaryApi.
+ *
+ * @param parameters the K8s Participant Parameters
+ * @param clElementHandler the ControlLoop Element Handler
+ * @return ParticipantIntermediaryApi
+ */
+ @Bean
+ public ParticipantIntermediaryApi participantIntermediaryApi(ParticipantK8sParameters parameters,
+ ControlLoopElementHandler clElementHandler) {
+ ParticipantIntermediaryApi intermediaryApi = new ParticipantIntermediaryFactory().createApiImplementation();
+ intermediaryApi.init(parameters.getIntermediaryParameters());
+ intermediaryApi.registerControlLoopElementListener(clElementHandler);
+ return intermediaryApi;
+ }
+}
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
new file mode 100644
index 000000000..427b06fc5
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/controller/ChartController.java
@@ -0,0 +1,166 @@
+/*-
+ * ========================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.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import java.io.IOException;
+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.InstallationInfo;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.service.ChartService;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestController("chartController")
+@RequestMapping("helm")
+@Api(tags = {"chart"})
+public class ChartController {
+
+ @Autowired
+ private ChartService chartService;
+
+ private static final StandardCoder CODER = new StandardCoder();
+
+ /**
+ * REST endpoint to get all the charts.
+ *
+ * @return List of charts installed
+ */
+ @GetMapping(path = "/charts", produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Return all Charts")
+ @ApiResponses(value = {@ApiResponse(code = 200, message = "chart List")})
+ public ResponseEntity<ChartList> getAllCharts() {
+ return new ResponseEntity<>(ChartList.builder().charts(new ArrayList<>(chartService.getAllCharts())).build(),
+ HttpStatus.OK);
+ }
+
+ /**
+ * REST endpoint to install a helm chart.
+ *
+ * @param info Info of the chart to be installed
+ * @return Status of the install operation
+ * @throws ServiceException incase of error
+ */
+ @PostMapping(path = "/install", consumes = MediaType.APPLICATION_JSON_VALUE,
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Install the chart")
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "chart Installed")})
+ public ResponseEntity<Object> installChart(@RequestBody InstallationInfo info)
+ throws ServiceException, IOException {
+ ChartInfo chart = chartService.getChart(info.getName(), info.getVersion());
+ if (chart == null) {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+
+ chartService.installChart(chart);
+ return new ResponseEntity<>(HttpStatus.CREATED);
+ }
+
+ /**
+ * REST endpoint to uninstall a specific chart.
+ *
+ * @param name name of the chart
+ * @param version version of the chart
+ * @return Status of operation
+ * @throws ServiceException incase of error.
+ */
+ @DeleteMapping(path = "/uninstall/{name}/{version}", produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Uninstall the Chart")
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "chart Uninstalled")})
+ public ResponseEntity<Object> uninstallChart(@PathVariable("name") String name,
+ @PathVariable("version") String version) throws ServiceException {
+ ChartInfo chart = chartService.getChart(name, version);
+ if (chart == null) {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+
+ chartService.uninstallChart(chart);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+
+ /**
+ * REST endpoint to onboard a chart.
+ *
+ * @param chartFile Multipart file for the helm chart
+ * @param infoJson AppInfo of the chart
+ * @return Status of onboard operation
+ * @throws ServiceException incase of error
+ * @throws IOException incase of IO error
+ */
+ @PostMapping(path = "/charts", consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Onboard the Chart")
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Chart Onboarded")})
+ public ResponseEntity<String> onboardChart(@RequestPart("chart") MultipartFile chartFile,
+ @RequestParam(name = "values", required = false) MultipartFile overrideFile,
+ @RequestParam("info") String infoJson) throws ServiceException, IOException {
+
+ ChartInfo info;
+ try {
+ info = CODER.decode(infoJson, ChartInfo.class);
+ } catch (CoderException e) {
+ throw new ServiceException("Error parsing the chart information", e);
+ }
+
+ chartService.saveChart(info, chartFile, overrideFile);
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ /**
+ * REST endpoint to delete a specific helm chart.
+ *
+ * @param name name of the chart
+ * @param version version of the chart
+ * @return Status of operation
+ * @throws ServiceException incase of error.
+ */
+ @DeleteMapping(path = "/charts/{name}/{version}")
+ @ApiOperation(value = "Delete the chart")
+ @ApiResponses(value = {@ApiResponse(code = 204, message = "Chart Deleted")})
+ public ResponseEntity<Object> deleteChart(@PathVariable("name") String name,
+ @PathVariable("version") String version) throws ServiceException {
+
+ ChartInfo chart = chartService.getChart(name, version);
+ if (chart == null) {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+
+ chartService.deleteChart(chart);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/exception/ServiceException.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/exception/ServiceException.java
new file mode 100644
index 000000000..9a825cf75
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/exception/ServiceException.java
@@ -0,0 +1,32 @@
+/*-
+ * ========================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.exception;
+
+public class ServiceException extends Exception {
+
+ private static final long serialVersionUID = 6810785674716590648L;
+
+ public ServiceException(String message) {
+ super(message);
+ }
+
+ public ServiceException(String message, Exception originalException) {
+ super(message, originalException);
+ }
+}
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
new file mode 100644
index 000000000..5f1dcb8d4
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/handler/ControlLoopElementHandler.java
@@ -0,0 +1,147 @@
+/*-
+ * ============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.handler;
+
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
+import org.onap.policy.clamp.controlloop.participant.intermediary.api.ControlLoopElementListener;
+import org.onap.policy.clamp.controlloop.participant.intermediary.api.ParticipantIntermediaryApi;
+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.service.ChartService;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class handles implementation of controlLoopElement updates.
+ */
+@Component
+public class ControlLoopElementHandler implements ControlLoopElementListener {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ @Autowired
+ private ChartService chartService;
+
+ @Autowired
+ private ParticipantIntermediaryApi intermediaryApi;
+
+ // Map of CLElement Id and installed Helm charts
+ private final Map<UUID, ChartInfo> chartMap = new HashMap<>();
+
+ /**
+ * Callback method to handle a control loop element state change.
+ *
+ * @param controlLoopElementId the ID of the control loop element
+ * @param currentState the current state of the control loop element
+ * @param newState the state to which the control loop element is changing to
+ */
+ @Override
+ public synchronized void controlLoopElementStateChange(UUID controlLoopElementId, ControlLoopState currentState,
+ ControlLoopOrderedState newState) {
+ switch (newState) {
+ case UNINITIALISED:
+ ChartInfo chart = chartMap.get(controlLoopElementId);
+ if (chart != null) {
+ LOGGER.info("Helm deployment to be deleted {} ", chart.getReleaseName());
+ try {
+ chartService.uninstallChart(chart);
+ intermediaryApi.updateControlLoopElementState(controlLoopElementId, newState,
+ ControlLoopState.UNINITIALISED);
+ } catch (ServiceException se) {
+ LOGGER.warn("deletion of Helm deployment failed", se);
+ }
+ }
+ break;
+ case PASSIVE:
+ intermediaryApi.updateControlLoopElementState(controlLoopElementId, newState, ControlLoopState.PASSIVE);
+ break;
+ case RUNNING:
+ intermediaryApi.updateControlLoopElementState(controlLoopElementId, newState, ControlLoopState.RUNNING);
+ break;
+ default:
+ LOGGER.warn("cannot transition from state {} to state {}", currentState, newState);
+ break;
+ }
+ }
+
+
+ /**
+ * Callback method to handle an update on a control loop element.
+ *
+ * @param element the information on the control loop element
+ * @param controlLoopDefinition toscaServiceTemplate
+ * @throws PfModelException in case of an exception
+ */
+ @Override
+ public synchronized void controlLoopElementUpdate(ControlLoopElement element,
+ ToscaServiceTemplate controlLoopDefinition) throws PfModelException {
+
+ for (Map.Entry<String, ToscaNodeTemplate> nodeTemplate : controlLoopDefinition.getToscaTopologyTemplate()
+ .getNodeTemplates().entrySet()) {
+
+ // Fetching the node template of corresponding CL element
+ if (element.getDefinition().getName().equals(nodeTemplate.getKey())
+ && nodeTemplate.getValue().getProperties().containsKey("chart")) {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> chartData =
+ (Map<String, Object>) nodeTemplate.getValue().getProperties().get("chart");
+
+ LOGGER.info("Installation request received for the Helm Chart {} ", chartData);
+ var chart = new ChartInfo(String.valueOf(chartData.get("release_name")),
+ String.valueOf(chartData.get("chart_name")), String.valueOf(chartData.get("version")),
+ String.valueOf(chartData.get("namespace")));
+ try {
+ var repositoryValue = chartData.get("repository");
+ if (repositoryValue != null) {
+ chart.setRepository(String.valueOf(repositoryValue));
+ }
+ chartService.installChart(chart);
+ chartMap.put(element.getId(), chart);
+ } catch (IOException | ServiceException ise) {
+ LOGGER.warn("installation of Helm chart failed", ise);
+ }
+ }
+ }
+ }
+
+ /**
+ * Overridden method.
+ *
+ * @param controlLoopElementId controlLoopElement id
+ * @throws PfModelException incase of error
+ */
+ @Override
+ public synchronized void handleStatistics(UUID controlLoopElementId) throws PfModelException {
+ // TODO Implement statistics functionality
+ }
+}
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
new file mode 100644
index 000000000..456122f3d
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/helm/HelmClient.java
@@ -0,0 +1,201 @@
+/*-
+ * ========================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.helm;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.invoke.MethodHandles;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+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.service.ChartStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Client to talk with Helm cli. Supports helm3 + version
+ */
+@Component
+public class HelmClient {
+
+ @Autowired
+ private ChartStore chartStore;
+
+ private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+
+ /**
+ * Install a chart.
+ *
+ * @param chart name and version.
+ * @throws ServiceException incase of error
+ */
+ public void installChart(ChartInfo chart) throws ServiceException {
+ var processBuilder = prepareCreateNamespaceCommand(chart.getNamespace());
+ try {
+ executeCommand(processBuilder);
+ } catch (ServiceException e) {
+ logger.warn("Namespace not created", e);
+ }
+ processBuilder = prepareInstallCommand(chart);
+ logger.info("Installing helm chart {} from the repository {} ", chart.getChartName(), chart.getRepository());
+ executeCommand(processBuilder);
+ logger.info("Chart {} installed successfully", chart.getChartName());
+ }
+
+ /**
+ * Finds helm chart repository for the chart.
+ *
+ * @param chart ChartInfo.
+ * @throws ServiceException incase of error
+ */
+ public String findChartRepository(ChartInfo chart) throws ServiceException, IOException {
+ updateHelmRepo();
+ logger.info("Looking for helm chart {} in all the configured helm repositories", chart.getChartName());
+ String repository = null;
+
+ var process = helmRepoVerifyCommand(chart.getChartName()).start();
+
+ try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
+ String line = reader.readLine();
+ while (line != null) {
+ if (line.contains(chart.getChartName())) {
+ repository = line.split("/")[0];
+ logger.info("Helm chart located in the repository {} ", repository);
+ return repository;
+ }
+ line = reader.readLine();
+ }
+ }
+
+ var localHelmChartDir = chartStore.getAppPath(chart.getChartName(), chart.getVersion()).toString();
+ logger.info("Chart not found in helm repositories, verifying local repo {} ", localHelmChartDir);
+ if (verifyLocalHelmRepo(localHelmChartDir + "/" + chart.getChartName())) {
+ repository = localHelmChartDir;
+ }
+
+ return repository;
+ }
+
+ /**
+ * Uninstall a chart.
+ *
+ * @param chart name and version.
+ * @throws ServiceException incase of error
+ */
+ public void uninstallChart(ChartInfo chart) throws ServiceException {
+ executeCommand(prepareUnInstallCommand(chart));
+ }
+
+ static String executeCommand(ProcessBuilder processBuilder) throws ServiceException {
+ var commandStr = toString(processBuilder);
+
+ processBuilder.redirectInput(ProcessBuilder.Redirect.DISCARD);
+
+ try {
+ var process = processBuilder.start();
+ process.waitFor();
+ int exitValue = process.exitValue();
+
+ if (exitValue != 0) {
+ var error = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8);
+ throw new ServiceException("Command execution failed: " + commandStr + " " + error);
+ }
+ var output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8);
+ logger.debug("Command <{}> execution, output: {}", commandStr, output);
+ return output;
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new ServiceException(
+ "Failed to execute the Command: " + commandStr + ", the command was interrupted", ie);
+ } catch (Exception exc) {
+ throw new ServiceException("Failed to execute the Command: " + commandStr, exc);
+ }
+ }
+
+ private ProcessBuilder prepareInstallCommand(ChartInfo chart) {
+
+ // @formatter:off
+ List<String> helmArguments = new ArrayList<>(
+ Arrays.asList(
+ "helm",
+ "install", chart.getReleaseName(), chart.getRepository() + "/" + chart.getChartName(),
+ "--version", chart.getVersion(),
+ "--namespace", chart.getNamespace()
+ )
+ );
+ // @formatter:on
+
+ // Verify if values.yaml available for the chart
+ var overrideFile = chartStore.getOverrideFile(chart).getPath();
+ if (verifyLocalHelmRepo(overrideFile)) {
+ logger.info("Override yaml file available for the helm chart");
+ helmArguments.addAll(Arrays.asList("--values", overrideFile));
+ }
+
+ return new ProcessBuilder().command(helmArguments);
+ }
+
+ private ProcessBuilder prepareUnInstallCommand(ChartInfo chart) {
+ return new ProcessBuilder("helm", "delete", chart.getReleaseName(), "--namespace", chart.getNamespace());
+ }
+
+ private ProcessBuilder prepareCreateNamespaceCommand(String namespace) {
+ return new ProcessBuilder().command("kubectl", "create", "namespace", namespace);
+ }
+
+ private ProcessBuilder helmRepoVerifyCommand(String chartName) {
+ return new ProcessBuilder().command("bash", "-c", "helm search repo | grep " + chartName);
+ }
+
+ private ProcessBuilder localRepoVerifyCommand(String localFile) {
+ return new ProcessBuilder().command("bash", "-c", "ls " + localFile);
+ }
+
+ private void updateHelmRepo() throws ServiceException {
+ logger.info("Updating local helm repositories before verifying the chart");
+ List<String> helmArguments = Arrays.asList("helm", "repo", "update");
+
+ executeCommand(new ProcessBuilder().command(helmArguments));
+ logger.debug("Helm repositories updated successfully");
+ }
+
+ private boolean verifyLocalHelmRepo(String localFile) {
+ var isVerified = false;
+ var processBuilder = localRepoVerifyCommand(localFile);
+ try {
+ executeCommand(processBuilder);
+ isVerified = true;
+ } catch (ServiceException e) {
+ logger.error("Unable to verify file in local repository", e);
+ }
+ return isVerified;
+ }
+
+ protected static String toString(ProcessBuilder processBuilder) {
+ return String.join(" ", processBuilder.command());
+ }
+}
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
new file mode 100644
index 000000000..6bfb7aed5
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartInfo.java
@@ -0,0 +1,45 @@
+/*-
+ * ========================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.Data;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import org.immutables.gson.Gson;
+
+@Data
+@RequiredArgsConstructor
+@Gson.TypeAdapters
+public class ChartInfo {
+
+ @NonNull
+ private String releaseName;
+
+ @NonNull
+ private String chartName;
+
+ @NonNull
+ private String version;
+
+ @NonNull
+ private String namespace;
+
+ private String repository;
+
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartList.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartList.java
new file mode 100644
index 000000000..c86bff58a
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartList.java
@@ -0,0 +1,31 @@
+/*-
+ * ========================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 java.util.Collection;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class ChartList {
+ private Collection<ChartInfo> charts;
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/InstallationInfo.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/InstallationInfo.java
new file mode 100644
index 000000000..b21e93a01
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/InstallationInfo.java
@@ -0,0 +1,29 @@
+/*-
+ * ========================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.Getter;
+import org.immutables.gson.Gson;
+
+@Getter
+@Gson.TypeAdapters
+public class InstallationInfo {
+ private String name;
+ private String version;
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/parameters/ParticipantK8sParameterHandler.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/parameters/ParticipantK8sParameterHandler.java
new file mode 100644
index 000000000..1a7dc35e8
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/parameters/ParticipantK8sParameterHandler.java
@@ -0,0 +1,73 @@
+/*-
+ * ============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.parameters;
+
+import java.io.File;
+import javax.ws.rs.core.Response;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+
+/**
+ * This class handles reading, parsing and validating of control loop participant parameters from JSON files.
+ */
+public class ParticipantK8sParameterHandler {
+ private static final Coder CODER = new StandardCoder();
+
+ /**
+ * Read the parameters from the path of the file.
+ *
+ * @param path path of the config file.
+ * @return the parameters read from the configuration file
+ * @throws ControlLoopException on parameter exceptions
+ */
+ public ParticipantK8sParameters toParticipantK8sParameters(String path) throws ControlLoopException {
+ ParticipantK8sParameters parameters = null;
+ // Read the parameters
+ try {
+ // Read the parameters from JSON
+ var file = new File(path);
+ parameters = CODER.decode(file, ParticipantK8sParameters.class);
+ } catch (final CoderException e) {
+ final String errorMessage =
+ "error reading parameters from \"" + path + "\"\n" + "(" + e.getClass().getSimpleName() + ")";
+ throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, errorMessage, e);
+ }
+
+ // The JSON processing returns null if there is an empty file
+ if (parameters == null) {
+ final String errorMessage = "no parameters found in \"" + path + "\"";
+ throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, errorMessage);
+ }
+
+ // validate the parameters
+ final BeanValidationResult validationResult = parameters.validate();
+ if (!validationResult.isValid()) {
+ String returnMessage =
+ "validation error(s) on parameters from \"" + path + "\"\n" + validationResult.getResult();
+ throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, returnMessage);
+ }
+
+ return parameters;
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/parameters/ParticipantK8sParameters.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/parameters/ParticipantK8sParameters.java
new file mode 100644
index 000000000..65b32433c
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/parameters/ParticipantK8sParameters.java
@@ -0,0 +1,59 @@
+/*-
+ * ============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.parameters;
+
+import javax.validation.constraints.NotBlank;
+import lombok.Getter;
+import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantIntermediaryParameters;
+import org.onap.policy.common.parameters.ParameterGroupImpl;
+import org.onap.policy.common.parameters.annotations.NotNull;
+import org.onap.policy.common.parameters.annotations.Valid;
+import org.onap.policy.models.provider.PolicyModelsProviderParameters;
+
+/**
+ * Class to hold all parameters needed for the kubernetes participant.
+ *
+ */
+@NotNull
+@NotBlank
+@Getter
+public class ParticipantK8sParameters extends ParameterGroupImpl {
+ public static final String DEFAULT_LOCAL_CHART_DIR = "/var/helm-manager/local-charts";
+ public static final String DEFAULT_INFO_FILE_NAME = "CHART_INFO.json";
+
+ @Valid
+ private ParticipantIntermediaryParameters intermediaryParameters;
+ @Valid
+ private PolicyModelsProviderParameters databaseProviderParameters;
+
+
+ private String localChartDirectory = DEFAULT_LOCAL_CHART_DIR;
+ private String infoFileName = DEFAULT_INFO_FILE_NAME;
+
+ /**
+ * Create the kubernetes participant parameter group.
+ *
+ * @param name the parameter group name
+ */
+ public ParticipantK8sParameters(final String name) {
+ super(name);
+ }
+}
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
new file mode 100644
index 000000000..6accac339
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartService.java
@@ -0,0 +1,122 @@
+/*-
+ * ========================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.service;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+@Service
+public class ChartService {
+ private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ @Autowired
+ private ChartStore chartStore;
+
+ @Autowired
+ private HelmClient helmClient;
+
+ /**
+ * Get all the installed charts.
+ * @return list of charts.
+ */
+ public Collection<ChartInfo> getAllCharts() {
+ return chartStore.getAllCharts();
+ }
+
+ /**
+ * Get specific chart info.
+ * @param name name of the app
+ * @param version version of the app
+ * @return chart
+ * @throws ServiceException incase of error.
+ */
+ public ChartInfo getChart(String name, String version) throws ServiceException {
+ return chartStore.getChart(name, version);
+ }
+
+ /**
+ * Save a helm chart.
+ * @param chartInfo name and version of the app.
+ * @param chartFile Helm chart file
+ * @return chart details of the helm chart
+ * @throws IOException incase of IO error
+ * @throws ServiceException incase of error
+ */
+ public ChartInfo saveChart(ChartInfo chartInfo, MultipartFile chartFile, MultipartFile overrideFile)
+ throws IOException, ServiceException {
+ return chartStore.saveChart(chartInfo, chartFile, overrideFile);
+ }
+
+ /**
+ * Delete a helm chart.
+ * @param chart name and version of the chart.
+ */
+ public void deleteChart(ChartInfo chart) {
+ chartStore.deleteChart(chart);
+ }
+
+ /**
+ * Install a helm chart.
+ * @param chart name and version.
+ * @throws ServiceException incase of error
+ */
+ public void installChart(ChartInfo chart) throws ServiceException, IOException {
+ if (chart.getRepository() == null) {
+ String repository = findChartRepo(chart);
+ if (repository == null) {
+ logger.error("Chart repository could not be found. Skipping chart Installation "
+ + "for the chart {} ", chart.getChartName());
+ return;
+ } else {
+ chart.setRepository(repository);
+ }
+ }
+ helmClient.installChart(chart);
+ }
+
+ /**
+ * Finds helm chart repository for a given chart.
+ * @param chart chartInfo.
+ * @throws ServiceException incase of error
+ */
+ public String findChartRepo(ChartInfo chart) throws ServiceException, IOException {
+ logger.info("Fetching helm chart repository for the given chart {} ", chart.getChartName());
+ return helmClient.findChartRepository(chart);
+ }
+
+ /**
+ * Uninstall a helm chart.
+ * @param chart name and version
+ * @throws ServiceException incase of error.
+ */
+ public void uninstallChart(ChartInfo chart) throws ServiceException {
+ logger.info("Uninstalling helm deployment {}", chart.getReleaseName());
+ helmClient.uninstallChart(chart);
+ }
+
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartStore.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartStore.java
new file mode 100644
index 000000000..2d0ce7a83
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartStore.java
@@ -0,0 +1,215 @@
+/*-
+ * ========================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.service;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.invoke.MethodHandles;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+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.parameters.ParticipantK8sParameters;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.FileSystemUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+@Component
+public class ChartStore {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private static final StandardCoder STANDARD_CODER = new StandardCoder();
+
+ @Autowired
+ private ParticipantK8sParameters participantK8sParameters;
+
+ /**
+ * The chartStore map contains chart name as key & ChartInfo as value.
+ */
+ private Map<String, ChartInfo> localChartMap = new ConcurrentHashMap<>();
+
+ /**
+ * Constructor method.
+ */
+ public ChartStore() {
+ this.restoreFromLocalFileSystem();
+ }
+
+ /**
+ * Get local helm chart file.
+ *
+ * @param chart ChartInfo
+ * @return the chart file.
+ */
+ public File getHelmChartFile(ChartInfo chart) {
+ var appPath = getAppPath(chart.getChartName(), chart.getVersion());
+ return new File(appPath.toFile(), chart.getChartName());
+ }
+
+ /**
+ * Get the override yaml file.
+ *
+ * @param chart ChartInfo
+ * @return the override yaml file
+ */
+ public File getOverrideFile(ChartInfo chart) {
+ var appPath = getAppPath(chart.getChartName(), chart.getVersion());
+ return new File(appPath.toFile(), "values.yaml");
+ }
+
+
+ /**
+ * Saves the helm chart.
+ *
+ * @param chartInfo chartInfo
+ * @param chartFile helm chart file.
+ * @return chart
+ * @throws IOException incase of IO error
+ * @throws ServiceException incase of error.
+ */
+ public synchronized ChartInfo saveChart(ChartInfo chartInfo, MultipartFile chartFile, MultipartFile overrideFile)
+ throws IOException, ServiceException {
+ if (localChartMap.containsKey(key(chartInfo.getChartName(), chartInfo.getVersion()))) {
+ throw new ServiceException("Chart already exist");
+ }
+ var appPath = getAppPath(chartInfo.getChartName(), chartInfo.getVersion());
+ Files.createDirectories(appPath);
+
+ chartFile.transferTo(getHelmChartFile(chartInfo));
+ if (overrideFile != null) {
+ overrideFile.transferTo(getOverrideFile(chartInfo));
+ }
+
+ localChartMap.put(key(chartInfo), chartInfo);
+ storeChartInFile(chartInfo);
+ return chartInfo;
+ }
+
+ /**
+ * Get the chart info.
+ *
+ * @param name name of the chart
+ * @param version version of the chart
+ * @return chart
+ */
+ public synchronized ChartInfo getChart(String name, String version) {
+ return localChartMap.get(key(name, version));
+ }
+
+ /**
+ * Get all the charts installed.
+ *
+ * @return list of charts.
+ */
+ public synchronized List<ChartInfo> getAllCharts() {
+ return new ArrayList<>(localChartMap.values());
+ }
+
+ /**
+ * Delete a chart.
+ *
+ * @param chart chart info
+ */
+ public synchronized void deleteChart(ChartInfo chart) {
+ var appPath = getAppPath(chart.getChartName(), chart.getVersion());
+ try {
+ FileSystemUtils.deleteRecursively(appPath);
+ } catch (IOException exc) {
+ LOGGER.warn("Could not delete chart from local file system : {}", appPath, exc);
+ }
+
+ localChartMap.remove(key(chart));
+ }
+
+ /**
+ * Fetch the local chart directory of specific chart.
+ *
+ * @param chartName name of the chart
+ * @param chartVersion version of the chart
+ * @return path
+ */
+ public Path getAppPath(String chartName, String chartVersion) {
+ return Path.of(participantK8sParameters.getLocalChartDirectory(), chartName, chartVersion);
+ }
+
+ private void storeChartInFile(ChartInfo chart) {
+ try (var out = new PrintStream(new FileOutputStream(getFile(chart)))) {
+ out.print(STANDARD_CODER.encode(chart));
+ } catch (Exception exc) {
+ LOGGER.warn("Could not store chart: {} {}", chart.getChartName(), exc);
+ }
+ }
+
+ private File getFile(ChartInfo chart) {
+ var appPath = getAppPath(chart.getChartName(), chart.getVersion()).toString();
+ return Path.of(appPath, participantK8sParameters.getInfoFileName()).toFile();
+ }
+
+ private synchronized void restoreFromLocalFileSystem() {
+ Path localChartDirectoryPath = Paths.get(participantK8sParameters.getLocalChartDirectory());
+
+ try {
+ Files.createDirectories(localChartDirectoryPath);
+ restoreFromLocalFileSystem(localChartDirectoryPath);
+ } catch (IOException ioe) {
+ LOGGER.warn("Could not restore charts from local file system: {}", ioe);
+ }
+ }
+
+ private synchronized void restoreFromLocalFileSystem(Path localChartDirectoryPath)
+ throws IOException {
+
+ Files.walkFileTree(localChartDirectoryPath, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path localChartFile, BasicFileAttributes attrs) throws IOException {
+ try {
+ ChartInfo chart = STANDARD_CODER.decode(localChartFile.toFile(), ChartInfo.class);
+ localChartMap.put(key(chart), chart);
+ return FileVisitResult.CONTINUE;
+ } catch (CoderException ce) {
+ throw new IOException("Error decoding chart file", ce);
+ }
+ }
+ });
+ }
+
+ private String key(ChartInfo chart) {
+ return key(chart.getChartName(), chart.getVersion());
+ }
+
+ private String key(String chartName, String chartVersion) {
+ return chartName + "_" + chartVersion;
+ }
+
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/resources/config/KubernetesParticipantConfig.json b/participant/participant-impl/participant-impl-kubernetes/src/main/resources/config/KubernetesParticipantConfig.json
new file mode 100644
index 000000000..620e05552
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/resources/config/KubernetesParticipantConfig.json
@@ -0,0 +1,56 @@
+{
+ "name": "ControlLoopParticipantK8s",
+ "localChartDirectory": "/var/helm-manager/local-charts",
+ "infoFileName": "CHART_INFO.json",
+
+ "intermediaryParameters":{
+ "name":"Participant parameters",
+ "reportingTimeInterval":120000,
+ "description":"Participant Description",
+ "participantId":{
+ "name":"K8sParticipant0",
+ "version":"1.0.0"
+ },
+ "participantType":{
+ "name":"org.onap.k8s.controlloop.K8SControlLoopParticipant",
+ "version":"2.3.4"
+ },
+ "clampControlLoopTopics":{
+ "topicSources":[
+ {
+ "topic":"POLICY-CLRUNTIME-PARTICIPANT",
+ "servers":[
+ "localhost"
+ ],
+ "topicCommInfrastructure":"dmaap",
+ "fetchTimeout":15000
+ }
+ ],
+ "topicSinks":[
+ {
+ "topic":"POLICY-CLRUNTIME-PARTICIPANT",
+ "servers":[
+ "localhost"
+ ],
+ "topicCommInfrastructure":"dmaap"
+ },
+ {
+ "topic":"POLICY-NOTIFICATION",
+ "servers":[
+ "localhost"
+ ],
+ "topicCommInfrastructure":"dmaap"
+ }
+ ]
+ }
+ },
+ "databaseProviderParameters":{
+ "name":"PolicyProviderParameterGroup",
+ "implementation":"org.onap.policy.models.provider.impl.DatabasePolicyModelsProviderImpl",
+ "databaseDriver":"org.mariadb.jdbc.Driver",
+ "databaseUrl":"jdbc:mariadb://localhost:3306/controlloop",
+ "databaseUser":"admin",
+ "databasePassword":"passme",
+ "persistenceUnit":"ToscaConceptTest"
+ }
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/resources/config/application.yaml b/participant/participant-impl/participant-impl-kubernetes/src/main/resources/config/application.yaml
new file mode 100644
index 000000000..b4240036b
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/main/resources/config/application.yaml
@@ -0,0 +1,25 @@
+spring:
+ profiles:
+ active: prod
+
+participant:
+ file: src/main/resources/config/KubernetesParticipantConfig.json
+management:
+ endpoints:
+ web:
+ exposure:
+ include: "loggers,logfile,health,info,metrics,threaddump,heapdump"
+server:
+ # Configuration of the HTTP/REST server. The parameters are defined and handled by the springboot framework.
+ # See springboot documentation.
+ http-port : 8083
+
+logging:
+ # Configuration of logging
+ level:
+ ROOT: INFO
+ org.springframework: ERROR
+ org.springframework.data: ERROR
+ org.springframework.web.reactive.function.client.ExchangeFunctions: ERROR
+ file:
+ name: /var/log/helm-manager/application.log
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/resources/KubernetesHelm.yaml b/participant/participant-impl/participant-impl-kubernetes/src/test/resources/KubernetesHelm.yaml
new file mode 100644
index 000000000..3212b5ad2
--- /dev/null
+++ b/participant/participant-impl/participant-impl-kubernetes/src/test/resources/KubernetesHelm.yaml
@@ -0,0 +1,154 @@
+# ============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=========================================================
+tosca_definitions_version: tosca_simple_yaml_1_3
+data_types:
+ onap.datatypes.ToscaConceptIdentifier:
+ derived_from: tosca.datatypes.Root
+ properties:
+ name:
+ type: string
+ required: true
+ version:
+ type: string
+ required: true
+node_types:
+ org.onap.policy.clamp.controlloop.Participant:
+ version: 1.0.1
+ derived_from: tosca.nodetypes.Root
+ properties:
+ provider:
+ type: string
+ requred: false
+ org.onap.policy.clamp.controlloop.ControlLoopElement:
+ version: 1.0.1
+ derived_from: tosca.nodetypes.Root
+ properties:
+ provider:
+ type: string
+ requred: false
+ participant_id:
+ type: onap.datatypes.ToscaConceptIdentifier
+ requred: true
+ org.onap.policy.clamp.controlloop.ControlLoop:
+ version: 1.0.1
+ derived_from: tosca.nodetypes.Root
+ properties:
+ provider:
+ type: string
+ requred: false
+ elements:
+ type: list
+ required: true
+ entry_schema:
+ type: onap.datatypes.ToscaConceptIdentifier
+ org.onap.policy.clamp.controlloop.K8SMicroserviceControlLoopElement:
+ version: 1.0.1
+ derived_from: org.onap.policy.clamp.controlloop.ControlLoopElement
+ properties:
+ chart:
+ type: string
+ required: true
+ configs:
+ type: list
+ required: false
+ requirements:
+ type: string
+ requred: false
+ templates:
+ type: list
+ required: false
+ entry_schema:
+ values:
+ type: string
+ requred: true
+topology_template:
+ node_templates:
+ org.onap.k8s.controlloop.K8SControlLoopParticipant:
+ version: 2.3.4
+ type: org.onap.policy.clamp.controlloop.Participant
+ type_version: 1.0.1
+ description: Participant for K8S
+ properties:
+ provider: ONAP
+
+ org.onap.domain.database.HelloWorld_K8SMicroserviceControlLoopElement:
+ # Chart from any chart repository configured on helm client.
+ version: 1.2.3
+ type: org.onap.policy.clamp.controlloop.K8SMicroserviceControlLoopElement
+ type_version: 1.0.0
+ description: Control loop element for the K8S microservice for Hello World
+ properties:
+ provider: ONAP
+ participant_id:
+ name: org.onap.k8s.controlloop.K8SControlLoopParticipant
+ version: 2.3.4
+ chart:
+ release_name: helloworld
+ chart_name: hello
+ version: 0.1.0
+ repository: chartMuseum
+ namespace: onap
+
+ org.onap.domain.database.PMSH_K8SMicroserviceControlLoopElement:
+ # Chart from local file system
+ version: 1.2.3
+ type: org.onap.policy.clamp.controlloop.K8SMicroserviceControlLoopElement
+ type_version: 1.0.0
+ description: Control loop element for the K8S microservice for PMSH
+ properties:
+ provider: ONAP
+ participant_id:
+ name: org.onap.k8s.controlloop.K8SControlLoopParticipant
+ version: 2.3.4
+ chart:
+ release_name: pmshmicroservice
+ chart_name: test
+ version: 1.0.1
+ namespace: onap
+
+ org.onap.domain.database.Local_K8SMicroserviceControlLoopElement:
+ # Chart installation without passing repository name
+ version: 1.2.3
+ type: org.onap.policy.clamp.controlloop.K8SMicroserviceControlLoopElement
+ type_version: 1.0.0
+ description: Control loop element for the K8S microservice for local chart
+ properties:
+ provider: ONAP
+ participant_id:
+ name: org.onap.k8s.controlloop.K8SControlLoopParticipant
+ version: 2.3.4
+ chart:
+ release_name: nginxms
+ chart_name: nginx-ingress
+ version: 0.9.1
+ namespace: onap
+
+ org.onap.domain.sample.GenericK8s_ControlLoopDefinition:
+ version: 1.2.3
+ type: org.onap.policy.clamp.controlloop.ControlLoop
+ type_version: 1.0.0
+ description: Control loop for Hello World
+ properties:
+ provider: ONAP
+ elements:
+ - name: org.onap.domain.database.HelloWorld_K8SMicroserviceControlLoopElement
+ version: 1.2.3
+ - name: org.onap.domain.database.PMSH_K8SMicroserviceControlLoopElement
+ version: 1.2.3
+ - name: org.onap.domain.database.Local_K8SMicroserviceControlLoopElement
+ version: 1.2.3