1186b7bf50d2856f9ea85321bed03cd1d117ab91
[policy/clamp.git] /
1 /*-
2  * ========================LICENSE_START=================================
3  * Copyright (C) 2021-2022 Nordix Foundation. All rights reserved.
4  * ======================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  * ========================LICENSE_END===================================
17  */
18
19 package org.onap.policy.clamp.acm.participant.kubernetes.controller;
20
21 import io.swagger.v3.oas.annotations.Operation;
22 import io.swagger.v3.oas.annotations.responses.ApiResponse;
23 import io.swagger.v3.oas.annotations.responses.ApiResponses;
24 import io.swagger.v3.oas.annotations.tags.Tag;
25 import java.io.IOException;
26 import java.lang.invoke.MethodHandles;
27 import java.util.ArrayList;
28 import lombok.RequiredArgsConstructor;
29 import org.onap.policy.clamp.acm.participant.kubernetes.exception.ServiceException;
30 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartInfo;
31 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartList;
32 import org.onap.policy.clamp.acm.participant.kubernetes.models.HelmRepository;
33 import org.onap.policy.clamp.acm.participant.kubernetes.models.InstallationInfo;
34 import org.onap.policy.clamp.acm.participant.kubernetes.service.ChartService;
35 import org.onap.policy.common.utils.coder.CoderException;
36 import org.onap.policy.common.utils.coder.StandardCoder;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
40 import org.springframework.http.HttpStatus;
41 import org.springframework.http.MediaType;
42 import org.springframework.http.ResponseEntity;
43 import org.springframework.web.bind.annotation.DeleteMapping;
44 import org.springframework.web.bind.annotation.GetMapping;
45 import org.springframework.web.bind.annotation.PathVariable;
46 import org.springframework.web.bind.annotation.PostMapping;
47 import org.springframework.web.bind.annotation.RequestBody;
48 import org.springframework.web.bind.annotation.RequestMapping;
49 import org.springframework.web.bind.annotation.RequestParam;
50 import org.springframework.web.bind.annotation.RequestPart;
51 import org.springframework.web.bind.annotation.RestController;
52 import org.springframework.web.multipart.MultipartFile;
53
54 @RequiredArgsConstructor
55 @RestController("chartController")
56 @ConditionalOnExpression("${chart.api.enabled:false}")
57 @RequestMapping("helm")
58 @Tag(name = "k8s-participant")
59 public class ChartController {
60     private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
61
62     private final ChartService chartService;
63
64     private static final StandardCoder CODER = new StandardCoder();
65
66     /**
67      * REST endpoint to get all the charts.
68      *
69      * @return List of charts installed
70      */
71     @GetMapping(path = "/charts", produces = MediaType.APPLICATION_JSON_VALUE)
72     @Operation(summary = "Return all Charts")
73     @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "chart List")})
74     public ResponseEntity<ChartList> getAllCharts() {
75         return new ResponseEntity<>(ChartList.builder().charts(new ArrayList<>(chartService.getAllCharts())).build(),
76                 HttpStatus.OK);
77     }
78
79     /**
80      * REST endpoint to install a helm chart.
81      *
82      * @param info Info of the chart to be installed
83      * @return Status of the install operation
84      * @throws ServiceException in case of error
85      * @throws IOException in case of IO error
86      */
87     @PostMapping(path = "/install", consumes = MediaType.APPLICATION_JSON_VALUE,
88             produces = MediaType.APPLICATION_JSON_VALUE)
89     @Operation(summary = "Install the chart")
90     @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "chart Installed")})
91     public ResponseEntity<Void> installChart(@RequestBody InstallationInfo info)
92             throws ServiceException, IOException {
93         ChartInfo chart = chartService.getChart(info.getName(), info.getVersion());
94         if (chart == null) {
95             return new ResponseEntity<>(HttpStatus.NOT_FOUND);
96         }
97
98         chartService.installChart(chart);
99         return new ResponseEntity<>(HttpStatus.CREATED);
100     }
101
102     /**
103      * REST endpoint to uninstall a specific chart.
104      *
105      * @param name name of the chart
106      * @param version version of the chart
107      * @return Status of operation
108      * @throws ServiceException in case of error.
109      */
110     @DeleteMapping(path = "/uninstall/{name}/{version}", produces = MediaType.APPLICATION_JSON_VALUE)
111     @Operation(summary = "Uninstall the Chart")
112     @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "chart Uninstalled")})
113     public ResponseEntity<Void> uninstallChart(@PathVariable("name") String name,
114             @PathVariable("version") String version) throws ServiceException {
115         ChartInfo chart = chartService.getChart(name, version);
116         if (chart == null) {
117             return new ResponseEntity<>(HttpStatus.NOT_FOUND);
118         }
119
120         chartService.uninstallChart(chart);
121         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
122     }
123
124     /**
125      * REST endpoint to onboard a chart.
126      *
127      * @param chartFile Multipart file for the helm chart
128      * @param infoJson AppInfo of the chart
129      * @param overrideFile the file for overriding the chart
130      * @return Status of onboard operation
131      * @throws ServiceException in case of error
132      * @throws IOException in case of IO error
133      */
134     @PostMapping(path = "/onboard/chart", consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
135             produces = MediaType.APPLICATION_JSON_VALUE)
136     @Operation(summary = "Onboard the Chart")
137     @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Chart Onboarded")})
138     public ResponseEntity<Void> onboardChart(@RequestPart("chart") MultipartFile chartFile,
139             @RequestParam(name = "values", required = false) MultipartFile overrideFile,
140             @RequestParam("info") String infoJson) throws ServiceException, IOException {
141
142         ChartInfo info;
143         try {
144             info = CODER.decode(infoJson, ChartInfo.class);
145         } catch (CoderException e) {
146             throw new ServiceException("Error parsing the chart information", e);
147         }
148
149         chartService.saveChart(info, chartFile, overrideFile);
150         return new ResponseEntity<>(HttpStatus.OK);
151     }
152
153     /**
154      * REST endpoint to delete a specific helm chart.
155      *
156      * @param name name of the chart
157      * @param version version of the chart
158      * @return Status of operation
159      */
160     @DeleteMapping(path = "/chart/{name}/{version}")
161     @Operation(summary = "Delete the chart")
162     @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Chart Deleted")})
163     public ResponseEntity<Void> deleteChart(@PathVariable("name") String name,
164             @PathVariable("version") String version) {
165
166         ChartInfo chart = chartService.getChart(name, version);
167         if (chart == null) {
168             return new ResponseEntity<>(HttpStatus.NOT_FOUND);
169         }
170
171         chartService.deleteChart(chart);
172         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
173     }
174
175     /**
176      * REST endpoint to configure a helm Repository.
177      *
178      * @param repo Helm repository to be configured
179      * @return Status of the operation
180      * @throws ServiceException in case of error
181      * @throws IOException in case of IO error
182      */
183     @PostMapping(path = "/repo", consumes = MediaType.APPLICATION_JSON_VALUE,
184             produces = MediaType.APPLICATION_JSON_VALUE)
185     @ApiResponses(
186             value = {@ApiResponse(responseCode = "201", description = "Repository added"),
187                 @ApiResponse(responseCode = "409", description = "Repository already Exist")})
188     public ResponseEntity<String> configureRepo(@RequestBody String repo)
189             throws ServiceException, IOException {
190         HelmRepository repository;
191         try {
192             repository = CODER.decode(repo, HelmRepository.class);
193         } catch (CoderException e) {
194             logger.warn("Error parsing the repository information:", e);
195             return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
196                     .body("Error parsing the repository information");
197         }
198         if (chartService.configureRepository(repository)) {
199             return new ResponseEntity<>(HttpStatus.CREATED);
200         }
201         return new ResponseEntity<>(HttpStatus.CONFLICT);
202     }
203 }