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