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