Improved helm-generator code to make it more testable and improved code coverage
[dcaegen2/platform.git] / mod2 / helm-generator / helmchartgenerator-core / src / main / java / org / onap / dcaegen2 / platform / helmchartgenerator / chartbuilder / ComponentSpecParser.java
1 /*
2  * # ============LICENSE_START=======================================================
3  * # Copyright (c) 2021 AT&T Intellectual Property. 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.dcaegen2.platform.helmchartgenerator.chartbuilder;
20
21 import org.onap.dcaegen2.platform.helmchartgenerator.Utils;
22 import org.onap.dcaegen2.platform.helmchartgenerator.models.chartinfo.ChartInfo;
23 import org.onap.dcaegen2.platform.helmchartgenerator.models.chartinfo.Metadata;
24 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.base.ComponentSpec;
25 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.common.Artifacts;
26 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.common.HealthCheck;
27 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.common.Parameters;
28 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.common.Policy;
29 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.common.PolicyInfo;
30 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.common.Self;
31 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.common.Service;
32 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.common.TlsInfo;
33 import org.onap.dcaegen2.platform.helmchartgenerator.models.componentspec.common.Volumes;
34 import org.onap.dcaegen2.platform.helmchartgenerator.validation.ComponentSpecValidator;
35 import org.springframework.beans.factory.annotation.Autowired;
36 import org.springframework.stereotype.Component;
37
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.nio.file.Paths;
41 import java.util.ArrayList;
42 import java.util.Collections;
43 import java.util.LinkedHashMap;
44 import java.util.List;
45 import java.util.Map;
46
47 /**
48  * ComponentSpecParser reads a componentspec file and collects useful data for helm chart generation.
49  * @author Dhrumin Desai
50  */
51 @Component
52 public class ComponentSpecParser {
53
54     @Autowired
55     private ComponentSpecValidator specValidator;
56
57     @Autowired
58     private Utils utils;
59
60     /**
61      * Constructor for ComponentSpecParser
62      * @param specValidator ComponentSpecValidator implementation
63      * @param utils
64      */
65     public ComponentSpecParser(ComponentSpecValidator specValidator, Utils utils) {
66         this.specValidator = specValidator;
67         this.utils = utils;
68     }
69
70     /**
71      *
72      * @param specFileLocation location of the application specification json file
73      * @param chartTemplateLocation location of the base helm chart template
74      * @param specSchemaLocation location of the specification json schema file to validate the application spec
75      * @return ChartInfo object populated with key-values
76      * @throws Exception
77      */
78     public ChartInfo extractChartInfo(String specFileLocation, String chartTemplateLocation, String specSchemaLocation) throws Exception {
79         specValidator.validateSpecFile(specFileLocation, specSchemaLocation);
80         ComponentSpec cs = utils.deserializeJsonFileToModel(specFileLocation, ComponentSpec.class);
81         ChartInfo chartInfo = new ChartInfo();
82         chartInfo.setMetadata(extractMetadata(cs.getSelf()));
83         chartInfo.setValues(extractValues(cs, chartTemplateLocation));
84         return chartInfo;
85     }
86
87     private Map<String, Object> extractValues(ComponentSpec cs, String chartTemplateLocation) {
88         Map<String, Object> outerValues = new LinkedHashMap<>();
89         if(cs.getAuxilary() != null && cs.getAuxilary().getTlsInfo() != null){
90             utils.putIfNotNull(outerValues,"certDirectory", cs.getAuxilary().getTlsInfo().getCertDirectory());
91             utils.putIfNotNull(outerValues, "tlsServer", cs.getAuxilary().getTlsInfo().getUseTls());
92         }
93         if(cs.getAuxilary() != null && cs.getAuxilary().getLogInfo() != null) {
94             utils.putIfNotNull(outerValues,"logDirectory", cs.getAuxilary().getLogInfo().get("log_directory"));
95         }
96         if(imageUriExistsForFirstArtifact(cs)){
97             utils.putIfNotNull(outerValues,"image", cs.getArtifacts()[0].getUri());
98         }
99         populateApplicationConfigSection(outerValues, cs);
100         populateReadinessSection(outerValues, cs);
101         populateApplicationEnvSection(outerValues, cs);
102         populateServiceSection(outerValues, cs);
103         populateCertificatesSection(outerValues, cs, chartTemplateLocation);
104         populatePoliciesSection(outerValues, cs);
105         populateExternalVolumesSection(outerValues, cs);
106         populatePostgresSection(outerValues, cs);
107         populateSecretsSection(outerValues, cs);
108         return outerValues;
109     }
110
111     private boolean imageUriExistsForFirstArtifact(ComponentSpec cs) {
112         final Artifacts[] artifacts = cs.getArtifacts();
113         return artifacts != null && artifacts.length > 0 && artifacts[0].getUri() != null;
114     }
115
116     private void populateApplicationConfigSection(Map<String, Object> outerValues, ComponentSpec cs) {
117         Map<String, Object> applicationConfig = new LinkedHashMap<>();
118          Parameters[] parameters = cs.getParameters();
119          for(Parameters param : parameters){
120             applicationConfig.put(param.getName(), param.getValue());
121          }
122         utils.putIfNotNull(outerValues,"applicationConfig", applicationConfig);
123     }
124
125     private void populateReadinessSection(Map<String, Object> outerValues, ComponentSpec cs) {
126
127         if (!healthCheckExists(cs)) return;
128
129         Map<String, Object> readiness = new LinkedHashMap<>();
130         final HealthCheck healthcheck = cs.getAuxilary().getHealthcheck();
131         utils.putIfNotNull(readiness, "initialDelaySeconds", healthcheck.getInitialDelaySeconds());
132
133         if(healthcheck.getInterval() != null) {
134             readiness.put("periodSeconds", getSeconds(healthcheck.getInterval(), "interval"));
135         }
136         if(healthcheck.getTimeout() != null) {
137             readiness.put("timeoutSeconds", getSeconds(healthcheck.getTimeout(), "timeout"));
138         }
139
140         readiness.put("path", healthcheck.getEndpoint());
141         readiness.put("scheme", healthcheck.getType());
142         readiness.put("port", healthcheck.getPort());
143
144         outerValues.put("readiness", readiness);
145     }
146
147     private int getSeconds(String value, String field) {
148         int seconds = 0;
149         try {
150             seconds = Integer.parseInt(value.replaceAll("[^\\d.]", ""));
151         }
152         catch (NumberFormatException e){
153             throw new RuntimeException(String.format("%s with %s is not given in a correct format", field, value));
154         }
155         return seconds;
156     }
157
158     private boolean healthCheckExists(ComponentSpec cs) {
159         return cs.getAuxilary() != null &&
160                 cs.getAuxilary().getHealthcheck() !=  null;
161     }
162
163     private void populateApplicationEnvSection(Map<String, Object> outerValues, ComponentSpec cs){
164         if(applicationEnvExists(cs)) {
165             Object applicationEnv = cs.getAuxilary().getHelm().getApplicationEnv();
166             utils.putIfNotNull(outerValues,"applicationEnv", applicationEnv);
167         }
168     }
169
170     private boolean applicationEnvExists(ComponentSpec cs) {
171         return cs.getAuxilary() != null &&
172                 cs.getAuxilary().getHelm() !=  null &&
173                 cs.getAuxilary().getHelm().getApplicationEnv() != null;
174     }
175
176     private void populateServiceSection(Map<String, Object> outerValues, ComponentSpec cs) {
177         if (!serviceExists(cs)) return;
178
179         Map<String, Object> service = new LinkedHashMap<>();
180         final Service serviceFromSpec = cs.getAuxilary().getHelm().getService();
181
182         if(serviceFromSpec.getPorts() != null){
183             List<Object> ports = mapServicePorts(serviceFromSpec.getPorts());
184             service.put("ports", ports);
185             utils.putIfNotNull(service, "type", serviceFromSpec.getType());
186         }
187         utils.putIfNotNull(service,"name", serviceFromSpec.getName());
188         utils.putIfNotNull(service,"has_internal_only_ports", serviceFromSpec.getHasInternalOnlyPorts());
189         outerValues.put("service", service);
190     }
191
192     private boolean serviceExists(ComponentSpec cs) {
193         return cs.getAuxilary() != null &&
194                 cs.getAuxilary().getHelm() !=  null &&
195                 cs.getAuxilary().getHelm().getService() !=  null;
196     }
197
198     private List<Object> mapServicePorts(Object[] ports) {
199         List<Object> portsList = new ArrayList<>();
200         Collections.addAll(portsList, ports);
201         return portsList;
202     }
203
204     private void populatePoliciesSection(Map<String, Object> outerValues, ComponentSpec cs) {
205         Map<String, Object> policies = new LinkedHashMap<>();
206         final PolicyInfo policyInfo = cs.getPolicyInfo();
207         if(policyInfo != null && policyInfo.getPolicy() != null) {
208             List<String> policyList = new ArrayList<>();
209             for (Policy policyItem : policyInfo.getPolicy()) {
210                 policyList.add('"' + policyItem.getPolicyID() + '"');
211             }
212             policies.put("policyRelease", "onap");
213             policies.put("duration", 300);
214             policies.put("policyID", "'" + policyList.toString() + "'\n");
215             outerValues.put("policies", policies);
216         }
217     }
218
219     private void populateCertificatesSection(Map<String, Object> outerValues, ComponentSpec cs, String chartTemplateLocation) {
220         Map<String, Object> certificate = new LinkedHashMap<>();
221         Map<String, Object> keystore = new LinkedHashMap<>();
222         Map<String, Object> passwordsSecretRef = new LinkedHashMap<>();
223         TlsInfo tlsInfo = cs.getAuxilary().getTlsInfo();
224         String componentName = getComponentNameWithOmitFirstWord(cs);
225         if(externalTlsExists(tlsInfo)) {
226             String mountPath = tlsInfo.getCertDirectory();
227             if(tlsInfo.getUseExternalTls() != null && tlsInfo.getUseExternalTls()) {
228                 checkCertificateYamlExists(chartTemplateLocation);
229                 mountPath += "external";
230             }
231             passwordsSecretRef.put("name", componentName + "-cmpv2-keystore-password");
232             passwordsSecretRef.put("key", "password");
233             passwordsSecretRef.put("create", true);
234             keystore.put("outputType", List.of("jks"));
235             keystore.put("passwordSecretRef", passwordsSecretRef);
236             certificate.put("mountPath", mountPath);
237             utils.putIfNotNull(certificate,"commonName", cs.getSelf().getName());
238             utils.putIfNotNull(certificate,"dnsNames", List.of(cs.getSelf().getName()));
239             certificate.put("keystore", keystore);
240             outerValues.put("certificates", List.of(certificate));
241         }
242     }
243
244     private String getComponentNameWithOmitFirstWord(ComponentSpec cs) {
245         return cs.getSelf().getName().substring(cs.getSelf().getName().indexOf("-") + 1);
246     }
247
248     private boolean externalTlsExists(TlsInfo tlsInfo) {
249         return tlsInfo != null && tlsInfo.getUseExternalTls() != null && tlsInfo.getUseExternalTls().equals(true);
250     }
251
252     private void checkCertificateYamlExists(String chartTemplateLocation) {
253         Path certificateYaml = Paths.get(chartTemplateLocation, "addons/templates/certificates.yaml");
254         if(!Files.exists(certificateYaml)) {
255             throw new RuntimeException("certificates.yaml not found under templates directory in addons");
256         }
257     }
258
259     private void populateExternalVolumesSection(Map<String, Object> outerValues, ComponentSpec cs) {
260         if(cs.getAuxilary().getVolumes() != null) {
261             List<Object> externalVolumes = new ArrayList<>();
262             Volumes[] volumes = cs.getAuxilary().getVolumes();
263             for (Volumes volume : volumes) {
264                 if(volume.getHost() == null) {
265                     Map<String, Object> tempVolume = new LinkedHashMap<>();
266                     tempVolume.put("name", volume.getConfigVolume().getName());
267                     tempVolume.put("type", "configMap");
268                     tempVolume.put("mountPath", volume.getContainer().getBind());
269                     tempVolume.put("optional", true);
270                     externalVolumes.add(tempVolume);
271                 }
272             }
273             if(!externalVolumes.isEmpty()) {
274                 outerValues.put("externalVolumes", externalVolumes);
275             }
276         }
277     }
278
279     private void populatePostgresSection(Map<String, Object> outerValues, ComponentSpec cs) {
280         if(cs.getAuxilary().getDatabases() != null) {
281             String componentFullName = cs.getSelf().getName();
282             String component = getComponentNameWithOmitFirstWord(cs);
283             Map<String, Object> postgres = new LinkedHashMap<>();
284             Map<String, Object> service = new LinkedHashMap<>();
285             Map<String, Object> container = new LinkedHashMap<>();
286             Map<String, Object> name = new LinkedHashMap<>();
287             Map<String, Object> persistence = new LinkedHashMap<>();
288             Map<String, Object> config = new LinkedHashMap<>();
289             service.put("name", componentFullName + "-postgres");
290             service.put("name2", componentFullName + "-pg-primary");
291             service.put("name3", componentFullName + "-pg-replica");
292             name.put("primary", componentFullName + "-pg-primary");
293             name.put("replica", componentFullName + "-pg-replica");
294             container.put("name", name);
295             persistence.put("mountSubPath", componentFullName + "/data");
296             persistence.put("mountInitPath", componentFullName);
297             config.put("pgUserName", component);
298             config.put("pgDatabase", component);
299             config.put("pgUserExternalSecret", "{{ include \"common.release\" . }}-" + component + "-pg-user-creds");
300
301             postgres.put("enabled", true);
302             postgres.put("nameOverride", componentFullName + "-postgres");
303             postgres.put("service", service);
304             postgres.put("container", container);
305             postgres.put("persistence", persistence);
306             postgres.put("config", config);
307             outerValues.put("postgres", postgres);
308         }
309     }
310
311     private void populateSecretsSection(Map<String, Object> outerValues, ComponentSpec cs) {
312         if(cs.getAuxilary().getDatabases() != null) {
313             String component = getComponentNameWithOmitFirstWord(cs);
314             List<Object> secrets = new ArrayList<>();
315             Map<String, Object> secret = new LinkedHashMap<>();
316             secret.put("uid", "pg-user-creds");
317             secret.put("name", "{{ include \"common.release\" . }}-" + component + "-pg-user-creds");
318             secret.put("type", "basicAuth");
319             secret.put("externalSecret", "{{ ternary \"\" (tpl (default \"\" .Values.postgres.config.pgUserExternalSecret) .) (hasSuffix \"" + component + "-pg-user-creds\" .Values.postgres.config.pgUserExternalSecret) }}");
320             secret.put("login", "{{ .Values.postgres.config.pgUserName }}");
321             secret.put("password", "{{ .Values.postgres.config.pgUserPassword }}");
322             secret.put("passwordPolicy", "generate");
323             secrets.add(secret);
324             outerValues.put("secrets", secrets);
325         }
326     }
327
328     private Metadata extractMetadata(Self self) {
329         Metadata metadata = new Metadata();
330         metadata.setName(self.getName());
331         metadata.setDescription(self.getDescription());
332         metadata.setVersion(self.getVersion());
333         return metadata;
334     }
335 }