ed53d0352964ea604f21ba0aaa3e837d1b9b72a1
[policy/clamp.git] /
1 /*-
2  * ========================LICENSE_START=================================
3  * Copyright (C) 2021 Nordix Foundation. All rights reserved.
4  * ======================================================================
5  * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
6  * ======================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ========================LICENSE_END===================================
19  */
20
21 package org.onap.policy.clamp.controlloop.participant.kubernetes.service;
22
23 import java.io.File;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.PrintStream;
27 import java.lang.invoke.MethodHandles;
28 import java.nio.file.FileVisitResult;
29 import java.nio.file.Files;
30 import java.nio.file.Path;
31 import java.nio.file.Paths;
32 import java.nio.file.SimpleFileVisitor;
33 import java.nio.file.attribute.BasicFileAttributes;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.concurrent.ConcurrentHashMap;
38 import lombok.AccessLevel;
39 import lombok.Getter;
40 import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException;
41 import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
42 import org.onap.policy.clamp.controlloop.participant.kubernetes.parameters.ParticipantK8sParameters;
43 import org.onap.policy.common.utils.coder.CoderException;
44 import org.onap.policy.common.utils.coder.StandardCoder;
45 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.stereotype.Component;
49 import org.springframework.util.FileSystemUtils;
50 import org.springframework.web.multipart.MultipartFile;
51
52 @Component
53 public class ChartStore {
54     private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
55
56     private static final StandardCoder STANDARD_CODER = new StandardCoder();
57
58     private final ParticipantK8sParameters participantK8sParameters;
59
60     // ChartStore map contains chart name as key & ChartInfo as value.
61     @Getter(AccessLevel.PACKAGE)
62     private Map<String, ChartInfo> localChartMap = new ConcurrentHashMap<>();
63
64     /**
65      * Constructor method.
66      */
67     public ChartStore(ParticipantK8sParameters participantK8sParameters) {
68         this.participantK8sParameters = participantK8sParameters;
69         this.restoreFromLocalFileSystem();
70     }
71
72     /**
73      * Get local helm chart file.
74      *
75      * @param chart ChartInfo
76      * @return the chart file.
77      */
78     public File getHelmChartFile(ChartInfo chart) {
79         var appPath = getAppPath(chart.getChartId());
80         return new File(appPath.toFile(), chart.getChartId().getName());
81     }
82
83     /**
84      * Get the override yaml file.
85      *
86      * @param chart ChartInfo
87      * @return the override yaml file
88      */
89     public File getOverrideFile(ChartInfo chart) {
90         var appPath = getAppPath(chart.getChartId());
91         return new File(appPath.toFile(), "values.yaml");
92     }
93
94
95     /**
96      * Saves the helm chart.
97      *
98      * @param chartInfo chartInfo
99      * @param chartFile helm chart file.
100      * @param overrideFile override file.
101      * @return chart
102      * @throws IOException incase of IO error
103      * @throws ServiceException incase of error.
104      */
105     public synchronized ChartInfo saveChart(ChartInfo chartInfo, MultipartFile chartFile, MultipartFile overrideFile)
106         throws IOException, ServiceException {
107         if (localChartMap.containsKey(key(chartInfo))) {
108             throw new ServiceException("Chart already exist");
109         }
110         var appPath = getAppPath(chartInfo.getChartId());
111         Files.createDirectories(appPath);
112
113         chartFile.transferTo(getHelmChartFile(chartInfo));
114         if (overrideFile != null) {
115             overrideFile.transferTo(getOverrideFile(chartInfo));
116         }
117
118         localChartMap.put(key(chartInfo), chartInfo);
119         storeChartInFile(chartInfo);
120         return chartInfo;
121     }
122
123     /**
124      * Get the chart info.
125      *
126      * @param name name of the chart
127      * @param version version of the chart
128      * @return chart
129      */
130     public synchronized ChartInfo getChart(String name, String version) {
131         return localChartMap.get(key(name, version));
132     }
133
134     /**
135      * Get all the charts installed.
136      *
137      * @return list of charts.
138      */
139     public synchronized List<ChartInfo> getAllCharts() {
140         return new ArrayList<>(localChartMap.values());
141     }
142
143     /**
144      * Delete a chart.
145      *
146      * @param chart chart info
147      */
148     public synchronized void deleteChart(ChartInfo chart) {
149         var appPath = getAppPath(chart.getChartId());
150         try {
151             FileSystemUtils.deleteRecursively(appPath);
152         } catch (IOException exc) {
153             LOGGER.warn("Could not delete chart from local file system : {}", appPath, exc);
154         }
155
156         localChartMap.remove(key(chart));
157     }
158
159     /**
160      * Fetch the local chart directory of specific chart.
161      *
162      * @param chartId Id of the chart
163      * @return path
164      */
165     public Path getAppPath(ToscaConceptIdentifier chartId) {
166         return Path.of(participantK8sParameters.getLocalChartDirectory(), chartId.getName(), chartId.getVersion());
167     }
168
169     private void storeChartInFile(ChartInfo chart) {
170         try (var out = new PrintStream(new FileOutputStream(getFile(chart)))) {
171             out.print(STANDARD_CODER.encode(chart));
172         } catch (Exception exc) {
173             LOGGER.warn("Could not store chart: {}", chart.getChartId(), exc);
174         }
175     }
176
177     private File getFile(ChartInfo chart) {
178         var appPath = getAppPath(chart.getChartId()).toString();
179         return Path.of(appPath, participantK8sParameters.getInfoFileName()).toFile();
180     }
181
182     private synchronized void restoreFromLocalFileSystem() {
183         try {
184             var localChartDirectoryPath = Paths.get(participantK8sParameters.getLocalChartDirectory());
185             Files.createDirectories(localChartDirectoryPath);
186             restoreFromLocalFileSystem(localChartDirectoryPath);
187         } catch (Exception ioe) {
188             LOGGER.warn("Could not restore charts from local file system", ioe);
189         }
190     }
191
192     private synchronized void restoreFromLocalFileSystem(Path localChartDirectoryPath)
193         throws IOException {
194
195         Files.walkFileTree(localChartDirectoryPath, new SimpleFileVisitor<Path>() {
196             @Override
197             public FileVisitResult visitFile(Path localChartFile, BasicFileAttributes attrs) throws IOException {
198                 try {
199                     // Decode only the json file excluding the helm charts
200                     if (localChartFile.endsWith(participantK8sParameters.getInfoFileName())) {
201                         ChartInfo chart = STANDARD_CODER.decode(localChartFile.toFile(), ChartInfo.class);
202                         localChartMap.put(key(chart), chart);
203                     }
204                     return FileVisitResult.CONTINUE;
205                 } catch (CoderException ce) {
206                     throw new IOException("Error decoding chart file", ce);
207                 }
208             }
209         });
210     }
211
212     private String key(ChartInfo chart) {
213         return key(chart.getChartId().getName(), chart.getChartId().getVersion());
214     }
215
216     private String key(String chartName, String chartVersion) {
217         return chartName + "_" + chartVersion;
218     }
219 }