dcdff62f589442786af3462ccd6333278ad6181a
[policy/clamp.git] /
1 /*-
2  * ========================LICENSE_START=================================
3  * Copyright (C) 2021 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.controlloop.participant.kubernetes.service;
20
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.PrintStream;
25 import java.lang.invoke.MethodHandles;
26 import java.nio.file.FileVisitResult;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.nio.file.SimpleFileVisitor;
31 import java.nio.file.attribute.BasicFileAttributes;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.concurrent.ConcurrentHashMap;
36 import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException;
37 import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
38 import org.onap.policy.clamp.controlloop.participant.kubernetes.parameters.ParticipantK8sParameters;
39 import org.onap.policy.common.utils.coder.CoderException;
40 import org.onap.policy.common.utils.coder.StandardCoder;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.springframework.stereotype.Component;
44 import org.springframework.util.FileSystemUtils;
45 import org.springframework.web.multipart.MultipartFile;
46
47 @Component
48 public class ChartStore {
49     private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
50
51     private static final StandardCoder STANDARD_CODER = new StandardCoder();
52
53     private final ParticipantK8sParameters participantK8sParameters;
54
55     /**
56      * The chartStore map contains chart name as key & ChartInfo as value.
57      */
58     private Map<String, ChartInfo> localChartMap = new ConcurrentHashMap<>();
59
60     /**
61      * Constructor method.
62      */
63     public ChartStore(ParticipantK8sParameters participantK8sParameters) {
64         this.participantK8sParameters = participantK8sParameters;
65         this.restoreFromLocalFileSystem();
66     }
67
68     /**
69      * Get local helm chart file.
70      *
71      * @param chart ChartInfo
72      * @return the chart file.
73      */
74     public File getHelmChartFile(ChartInfo chart) {
75         var appPath = getAppPath(chart.getChartName(), chart.getVersion());
76         return new File(appPath.toFile(), chart.getChartName());
77     }
78
79     /**
80      * Get the override yaml file.
81      *
82      * @param chart ChartInfo
83      * @return the override yaml file
84      */
85     public File getOverrideFile(ChartInfo chart) {
86         var appPath = getAppPath(chart.getChartName(), chart.getVersion());
87         return new File(appPath.toFile(), "values.yaml");
88     }
89
90
91     /**
92      * Saves the helm chart.
93      *
94      * @param chartInfo chartInfo
95      * @param chartFile helm chart file.
96      * @param overrideFile override file.
97      * @return chart
98      * @throws IOException incase of IO error
99      * @throws ServiceException incase of error.
100      */
101     public synchronized ChartInfo saveChart(ChartInfo chartInfo, MultipartFile chartFile, MultipartFile overrideFile)
102             throws IOException, ServiceException {
103         if (localChartMap.containsKey(key(chartInfo.getChartName(), chartInfo.getVersion()))) {
104             throw new ServiceException("Chart already exist");
105         }
106         var appPath = getAppPath(chartInfo.getChartName(), chartInfo.getVersion());
107         Files.createDirectories(appPath);
108
109         chartFile.transferTo(getHelmChartFile(chartInfo));
110         if (overrideFile != null) {
111             overrideFile.transferTo(getOverrideFile(chartInfo));
112         }
113
114         localChartMap.put(key(chartInfo), chartInfo);
115         storeChartInFile(chartInfo);
116         return chartInfo;
117     }
118
119     /**
120      * Get the chart info.
121      *
122      * @param name name of the chart
123      * @param version version of the chart
124      * @return chart
125      */
126     public synchronized ChartInfo getChart(String name, String version) {
127         return localChartMap.get(key(name, version));
128     }
129
130     /**
131      * Get all the charts installed.
132      *
133      * @return list of charts.
134      */
135     public synchronized List<ChartInfo> getAllCharts() {
136         return new ArrayList<>(localChartMap.values());
137     }
138
139     /**
140      * Delete a chart.
141      *
142      * @param chart chart info
143      */
144     public synchronized void deleteChart(ChartInfo chart) {
145         var appPath = getAppPath(chart.getChartName(), chart.getVersion());
146         try {
147             FileSystemUtils.deleteRecursively(appPath);
148         } catch (IOException exc) {
149             LOGGER.warn("Could not delete chart from local file system : {}", appPath, exc);
150         }
151
152         localChartMap.remove(key(chart));
153     }
154
155     /**
156      * Fetch the local chart directory of specific chart.
157      *
158      * @param chartName name of the chart
159      * @param chartVersion version of the chart
160      * @return path
161      */
162     public Path getAppPath(String chartName, String chartVersion) {
163         return Path.of(participantK8sParameters.getLocalChartDirectory(), chartName, chartVersion);
164     }
165
166     private void storeChartInFile(ChartInfo chart) {
167         try (var out = new PrintStream(new FileOutputStream(getFile(chart)))) {
168             out.print(STANDARD_CODER.encode(chart));
169         } catch (Exception exc) {
170             LOGGER.warn("Could not store chart: {} {}", chart.getChartName(), exc);
171         }
172     }
173
174     private File getFile(ChartInfo chart) {
175         var appPath = getAppPath(chart.getChartName(), chart.getVersion()).toString();
176         return Path.of(appPath, participantK8sParameters.getInfoFileName()).toFile();
177     }
178
179     private synchronized void restoreFromLocalFileSystem() {
180         try {
181             Path localChartDirectoryPath = Paths.get(participantK8sParameters.getLocalChartDirectory());
182             Files.createDirectories(localChartDirectoryPath);
183             restoreFromLocalFileSystem(localChartDirectoryPath);
184         } catch (Exception ioe) {
185             LOGGER.warn("Could not restore charts from local file system: {}", ioe);
186         }
187     }
188
189     private synchronized void restoreFromLocalFileSystem(Path localChartDirectoryPath)
190             throws IOException {
191
192         Files.walkFileTree(localChartDirectoryPath, new SimpleFileVisitor<Path>() {
193             @Override
194             public FileVisitResult visitFile(Path localChartFile, BasicFileAttributes attrs) throws IOException {
195                 try {
196                     // Decode only the json file excluding the helm charts
197                     if (localChartFile.endsWith(participantK8sParameters.getInfoFileName())) {
198                         ChartInfo chart = STANDARD_CODER.decode(localChartFile.toFile(), ChartInfo.class);
199                         localChartMap.put(key(chart), chart);
200                     }
201                     return FileVisitResult.CONTINUE;
202                 } catch (CoderException ce) {
203                     throw new IOException("Error decoding chart file", ce);
204                 }
205             }
206         });
207     }
208
209     private String key(ChartInfo chart) {
210         return key(chart.getChartName(), chart.getVersion());
211     }
212
213     private String key(String chartName, String chartVersion) {
214         return chartName + "_" + chartVersion;
215     }
216
217 }