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
9 * # http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
19 package org.onap.dcaegen2.platform.helmchartgenerator.chartbuilder;
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;
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;
52 * ComponentSpecParser reads a componentspec file and collects useful data for helm chart generation.
53 * @author Dhrumin Desai
57 public class ComponentSpecParser {
60 private ComponentSpecValidator specValidator;
66 * Constructor for ComponentSpecParser
67 * @param specValidator ComponentSpecValidator implementation
70 public ComponentSpecParser(ComponentSpecValidator specValidator, Utils utils) {
71 this.specValidator = specValidator;
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
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));
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());
98 if(cs.getAuxilary() != null && cs.getAuxilary().getLogInfo() != null) {
99 utils.putIfNotNull(outerValues,"logDirectory", cs.getAuxilary().getLogInfo().get("log_directory"));
101 if(imageUriExistsForFirstArtifact(cs)){
102 utils.putIfNotNull(outerValues,"image", cs.getArtifacts()[0].getUri());
104 populateApplicationConfigSection(outerValues, cs);
105 populateReadinessSection(outerValues, cs);
106 populateApplicationEnvSection(outerValues, cs);
107 populateServiceSection(outerValues, cs);
108 populateCertificatesSection(outerValues, cs, chartTemplateLocation);
109 populatePoliciesSection(outerValues, cs);
110 populateExternalVolumesSection(outerValues, cs);
111 populatePostgresSection(outerValues, cs);
112 populateSecretsSection(outerValues, cs);
116 private boolean imageUriExistsForFirstArtifact(ComponentSpec cs) {
117 final Artifacts[] artifacts = cs.getArtifacts();
118 return artifacts != null && artifacts.length > 0 && artifacts[0].getUri() != null;
121 private void populateApplicationConfigSection(Map<String, Object> outerValues, ComponentSpec cs) {
122 Map<String, Object> applicationConfig = new LinkedHashMap<>();
123 Parameters[] parameters = cs.getParameters();
124 for(Parameters param : parameters){
125 if (Arrays.asList("streams_publishes", "streams_subscribes").contains(param.getName())){
126 applicationConfig.put(param.getName(), parseStringToMap(param.getValue()));
129 applicationConfig.put(param.getName(), param.getValue());
132 utils.putIfNotNull(outerValues,"applicationConfig", applicationConfig);
135 private Object parseStringToMap(Object value) {
136 if (value instanceof String){
138 return new ObjectMapper().readValue((String)value, Map.class);
139 } catch (JsonProcessingException e) {
140 log.error(e.getMessage(), e);
141 log.warn("could not parse streams_publishes / streams_subscribes. Default value will be used.");
147 private void populateReadinessSection(Map<String, Object> outerValues, ComponentSpec cs) {
149 if (!healthCheckExists(cs)) return;
151 Map<String, Object> readiness = new LinkedHashMap<>();
152 final HealthCheck healthcheck = cs.getAuxilary().getHealthcheck();
153 utils.putIfNotNull(readiness, "initialDelaySeconds", healthcheck.getInitialDelaySeconds());
155 if(healthcheck.getInterval() != null) {
156 readiness.put("periodSeconds", getSeconds(healthcheck.getInterval(), "interval"));
158 if(healthcheck.getTimeout() != null) {
159 readiness.put("timeoutSeconds", getSeconds(healthcheck.getTimeout(), "timeout"));
162 readiness.put("path", healthcheck.getEndpoint());
163 readiness.put("scheme", healthcheck.getType());
164 readiness.put("port", healthcheck.getPort());
166 outerValues.put("readiness", readiness);
169 private int getSeconds(String value, String field) {
172 seconds = Integer.parseInt(value.replaceAll("[^\\d.]", ""));
174 catch (NumberFormatException e){
175 throw new RuntimeException(String.format("%s with %s is not given in a correct format", field, value));
180 private boolean healthCheckExists(ComponentSpec cs) {
181 return cs.getAuxilary() != null &&
182 cs.getAuxilary().getHealthcheck() != null;
185 private void populateApplicationEnvSection(Map<String, Object> outerValues, ComponentSpec cs){
186 if(applicationEnvExists(cs)) {
187 Object applicationEnv = cs.getAuxilary().getHelm().getApplicationEnv();
188 utils.putIfNotNull(outerValues,"applicationEnv", applicationEnv);
192 private boolean applicationEnvExists(ComponentSpec cs) {
193 return cs.getAuxilary() != null &&
194 cs.getAuxilary().getHelm() != null &&
195 cs.getAuxilary().getHelm().getApplicationEnv() != null;
198 private void populateServiceSection(Map<String, Object> outerValues, ComponentSpec cs) {
199 if (!serviceExists(cs)) return;
201 Map<String, Object> service = new LinkedHashMap<>();
202 final Service serviceFromSpec = cs.getAuxilary().getHelm().getService();
204 if(serviceFromSpec.getPorts() != null){
205 List<Object> ports = mapServicePorts(serviceFromSpec.getPorts());
206 service.put("ports", ports);
207 utils.putIfNotNull(service, "type", serviceFromSpec.getType());
209 utils.putIfNotNull(service,"name", serviceFromSpec.getName());
210 utils.putIfNotNull(service,"has_internal_only_ports", serviceFromSpec.getHasInternalOnlyPorts());
211 outerValues.put("service", service);
214 private boolean serviceExists(ComponentSpec cs) {
215 return cs.getAuxilary() != null &&
216 cs.getAuxilary().getHelm() != null &&
217 cs.getAuxilary().getHelm().getService() != null;
220 private List<Object> mapServicePorts(Object[] ports) {
221 List<Object> portsList = new ArrayList<>();
222 Collections.addAll(portsList, ports);
226 private void populatePoliciesSection(Map<String, Object> outerValues, ComponentSpec cs) {
227 Map<String, Object> policies = new LinkedHashMap<>();
228 final PolicyInfo policyInfo = cs.getPolicyInfo();
229 if(policyInfo != null && policyInfo.getPolicy() != null) {
230 List<String> policyList = new ArrayList<>();
231 for (Policy policyItem : policyInfo.getPolicy()) {
232 policyList.add('"' + policyItem.getPolicyID() + '"');
234 policies.put("policyRelease", "onap");
235 policies.put("duration", 300);
236 policies.put("policyID", "'" + policyList.toString() + "'\n");
237 outerValues.put("policies", policies);
241 private void populateCertificatesSection(Map<String, Object> outerValues, ComponentSpec cs, String chartTemplateLocation) {
242 Map<String, Object> certificate = new LinkedHashMap<>();
243 Map<String, Object> keystore = new LinkedHashMap<>();
244 Map<String, Object> passwordsSecretRef = new LinkedHashMap<>();
245 TlsInfo tlsInfo = cs.getAuxilary().getTlsInfo();
246 String componentName = getComponentNameWithOmitFirstWord(cs);
247 if(externalTlsExists(tlsInfo)) {
248 String mountPath = tlsInfo.getCertDirectory();
249 if(tlsInfo.getUseExternalTls() != null && tlsInfo.getUseExternalTls()) {
250 checkCertificateYamlExists(chartTemplateLocation);
251 mountPath += "external";
253 passwordsSecretRef.put("name", componentName + "-cmpv2-keystore-password");
254 passwordsSecretRef.put("key", "password");
255 passwordsSecretRef.put("create", true);
256 keystore.put("outputType", List.of("jks"));
257 keystore.put("passwordSecretRef", passwordsSecretRef);
258 certificate.put("mountPath", mountPath);
259 utils.putIfNotNull(certificate,"commonName", cs.getSelf().getName());
260 utils.putIfNotNull(certificate,"dnsNames", List.of(cs.getSelf().getName()));
261 certificate.put("keystore", keystore);
262 outerValues.put("certificates", List.of(certificate));
266 private String getComponentNameWithOmitFirstWord(ComponentSpec cs) {
267 return cs.getSelf().getName().substring(cs.getSelf().getName().indexOf("-") + 1);
270 private boolean externalTlsExists(TlsInfo tlsInfo) {
271 return tlsInfo != null && tlsInfo.getUseExternalTls() != null && tlsInfo.getUseExternalTls().equals(true);
274 private void checkCertificateYamlExists(String chartTemplateLocation) {
275 Path certificateYaml = Paths.get(chartTemplateLocation, "addons/templates/certificates.yaml");
276 if(!Files.exists(certificateYaml)) {
277 throw new RuntimeException("certificates.yaml not found under templates directory in addons");
281 private void populateExternalVolumesSection(Map<String, Object> outerValues, ComponentSpec cs) {
282 if(cs.getAuxilary().getVolumes() != null) {
283 List<Object> externalVolumes = new ArrayList<>();
284 Volumes[] volumes = cs.getAuxilary().getVolumes();
285 for (Volumes volume : volumes) {
286 if(volume.getHost() == null) {
287 Map<String, Object> tempVolume = new LinkedHashMap<>();
288 tempVolume.put("name", volume.getConfigVolume().getName());
289 tempVolume.put("type", "configMap");
290 tempVolume.put("mountPath", volume.getContainer().getBind());
291 tempVolume.put("optional", true);
292 externalVolumes.add(tempVolume);
295 if(!externalVolumes.isEmpty()) {
296 outerValues.put("externalVolumes", externalVolumes);
301 private void populatePostgresSection(Map<String, Object> outerValues, ComponentSpec cs) {
302 if(cs.getAuxilary().getDatabases() != null) {
303 String componentFullName = cs.getSelf().getName();
304 String component = getComponentNameWithOmitFirstWord(cs);
305 Map<String, Object> postgres = new LinkedHashMap<>();
306 Map<String, Object> service = new LinkedHashMap<>();
307 Map<String, Object> container = new LinkedHashMap<>();
308 Map<String, Object> name = new LinkedHashMap<>();
309 Map<String, Object> persistence = new LinkedHashMap<>();
310 Map<String, Object> config = new LinkedHashMap<>();
311 service.put("name", componentFullName + "-postgres");
312 service.put("name2", componentFullName + "-pg-primary");
313 service.put("name3", componentFullName + "-pg-replica");
314 name.put("primary", componentFullName + "-pg-primary");
315 name.put("replica", componentFullName + "-pg-replica");
316 container.put("name", name);
317 persistence.put("mountSubPath", componentFullName + "/data");
318 persistence.put("mountInitPath", componentFullName);
319 config.put("pgUserName", component);
320 config.put("pgDatabase", component);
321 config.put("pgUserExternalSecret", "{{ include \"common.release\" . }}-" + component + "-pg-user-creds");
323 postgres.put("enabled", true);
324 postgres.put("nameOverride", componentFullName + "-postgres");
325 postgres.put("service", service);
326 postgres.put("container", container);
327 postgres.put("persistence", persistence);
328 postgres.put("config", config);
329 outerValues.put("postgres", postgres);
333 private void populateSecretsSection(Map<String, Object> outerValues, ComponentSpec cs) {
334 if(cs.getAuxilary().getDatabases() != null) {
335 String component = getComponentNameWithOmitFirstWord(cs);
336 List<Object> secrets = new ArrayList<>();
337 Map<String, Object> secret = new LinkedHashMap<>();
338 secret.put("uid", "pg-user-creds");
339 secret.put("name", "{{ include \"common.release\" . }}-" + component + "-pg-user-creds");
340 secret.put("type", "basicAuth");
341 secret.put("externalSecret", "{{ ternary \"\" (tpl (default \"\" .Values.postgres.config.pgUserExternalSecret) .) (hasSuffix \"" + component + "-pg-user-creds\" .Values.postgres.config.pgUserExternalSecret) }}");
342 secret.put("login", "{{ .Values.postgres.config.pgUserName }}");
343 secret.put("password", "{{ .Values.postgres.config.pgUserPassword }}");
344 secret.put("passwordPolicy", "generate");
346 outerValues.put("secrets", secrets);
350 private Metadata extractMetadata(Self self) {
351 Metadata metadata = new Metadata();
352 metadata.setName(self.getName());
353 metadata.setDescription(self.getDescription());
354 metadata.setVersion(self.getVersion());