aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/org/onap/sdc/helmvalidator/helm/validation/ValidationService.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/onap/sdc/helmvalidator/helm/validation/ValidationService.java')
-rw-r--r--src/main/java/org/onap/sdc/helmvalidator/helm/validation/ValidationService.java221
1 files changed, 221 insertions, 0 deletions
diff --git a/src/main/java/org/onap/sdc/helmvalidator/helm/validation/ValidationService.java b/src/main/java/org/onap/sdc/helmvalidator/helm/validation/ValidationService.java
new file mode 100644
index 0000000..1e6cedb
--- /dev/null
+++ b/src/main/java/org/onap/sdc/helmvalidator/helm/validation/ValidationService.java
@@ -0,0 +1,221 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC-HELM-VALIDATOR
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.sdc.helmvalidator.helm.validation;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.onap.sdc.helmvalidator.helm.validation.exception.BashExecutionException;
+import org.onap.sdc.helmvalidator.helm.validation.exception.NotSupportedVersionException;
+import org.onap.sdc.helmvalidator.helm.validation.model.BashOutput;
+import org.onap.sdc.helmvalidator.helm.validation.model.LintValidationResult;
+import org.onap.sdc.helmvalidator.helm.validation.model.TemplateValidationResult;
+import org.onap.sdc.helmvalidator.helm.validation.model.ValidationResult;
+import org.onap.sdc.helmvalidator.helm.versions.ChartBasedVersionProvider;
+import org.onap.sdc.helmvalidator.helm.versions.SupportedVersionsProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+@Service
+public class ValidationService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ValidationService.class);
+ private static final String TEMPLATE_OPTION = "template";
+ private static final String LINT_OPTION = "lint";
+ private static final String HELM_SUMMARY_MESSAGE_PATTERN =
+ "Error: \\d* chart\\(s\\) linted, \\d* chart\\(s\\) failed";
+ private static final boolean INVALID_RESULT = false;
+
+ private final FileManager fileManager;
+
+ private final BashExecutor executor;
+
+ private final SupportedVersionsProvider supportedVersionsProvider;
+
+ private final ChartBasedVersionProvider chartBasedVersionProvider;
+
+
+ /**
+ * Constructor for ValidationService.
+ *
+ * @param fileManager object responsible for file manging
+ * @param executor object responsible for running shell commands
+ * @param supportedVersionsProvider object providing supported versions of Helm
+ * @param chartBasedVersionProvider object allowing to derive Helm version from a chart
+ */
+ @Autowired
+ public ValidationService(
+ FileManager fileManager, BashExecutor executor,
+ SupportedVersionsProvider supportedVersionsProvider,
+ ChartBasedVersionProvider chartBasedVersionProvider) {
+ this.fileManager = fileManager;
+ this.executor = executor;
+ this.supportedVersionsProvider = supportedVersionsProvider;
+ this.chartBasedVersionProvider = chartBasedVersionProvider;
+ }
+
+ /**
+ * Process Helm chart package with given options.
+ *
+ * @param desiredVersion requested version of Helm client to be used
+ * @param file packaged Helm chart file
+ * @param isLinted flag deciding if chart should be linted
+ * @param isStrictLinted flag deciding if chart should be linted with strict option turned on
+ * @return Result of Helm chart validation
+ */
+ public ValidationResult process(String desiredVersion, MultipartFile file, boolean isLinted,
+ boolean isStrictLinted) {
+ String chartPath = fileManager.saveFile(file);
+ try {
+ String helmVersion = getSupportedHelmVersion(desiredVersion, chartPath);
+ return validateChart(helmVersion, file, isLinted, isStrictLinted, chartPath);
+ } finally {
+ LOGGER.info("File process finished");
+ fileManager.removeFile(chartPath);
+ }
+ }
+
+ private String getSupportedHelmVersion(String desiredVersion, String chartPath) {
+ String helmVersion = getHelmVersion(desiredVersion, chartPath);
+
+ if (isNotSupportedVersion(helmVersion)) {
+ throw new NotSupportedVersionException(helmVersion);
+ }
+
+ return helmVersion;
+ }
+
+ private String getHelmVersion(String desiredVersion, String chartPath) {
+ if (desiredVersion == null) {
+ return chartBasedVersionProvider.getVersion(chartPath);
+ }
+
+ if (desiredVersion.startsWith("v")) {
+ String helmMajorVersion = desiredVersion.substring(1);
+ return supportedVersionsProvider.getLatestVersion(helmMajorVersion);
+ }
+
+ return desiredVersion;
+ }
+
+ private ValidationResult validateChart(String version, MultipartFile file, boolean isLinted, boolean isStrictLinted,
+ String chartPath) {
+ LOGGER.info("Start validation of file: {}, with helm version: {}",
+ file.getOriginalFilename(), version);
+
+ TemplateValidationResult templateValidationResult = runHelmTemplate(
+ buildHelmTemplateCommand(version, chartPath));
+ LOGGER.info("Helm template finished");
+
+ if (isLinted) {
+ LOGGER.info("Start helm lint, strict: {}", isStrictLinted);
+ LintValidationResult lintValidationResult = runHelmLint(
+ buildHelmLintCommand(version, chartPath, isStrictLinted));
+ LOGGER.info("Helm lint finished");
+ return new ValidationResult(templateValidationResult, lintValidationResult, version);
+ }
+
+ return new ValidationResult(templateValidationResult, version);
+ }
+
+ private boolean isNotSupportedVersion(String version) {
+ return !supportedVersionsProvider.getVersions().contains(version);
+ }
+
+
+ private String buildHelmTemplateCommand(String version, String chartPath) {
+ return "helm-v" + version + " " + TEMPLATE_OPTION + " " + chartPath;
+ }
+
+ private TemplateValidationResult runHelmTemplate(String helmCommand)
+ throws BashExecutionException {
+
+ LOGGER.debug("Command executions: {} ", helmCommand);
+ BashOutput chartTemplateResult = executor.execute(helmCommand);
+ LOGGER.debug("Status code: " + chartTemplateResult.getExitValue());
+ if (chartTemplateResult.getExitValue() != 0) {
+ List<String> renderingErrors = parseTemplateError(chartTemplateResult.getOutputLines());
+ return new TemplateValidationResult(false, renderingErrors);
+ }
+ return new TemplateValidationResult(true, Collections.emptyList());
+ }
+
+ private String buildHelmLintCommand(String version, String chartPath, boolean isStrictLint) {
+ String command = "helm-v" + version + " " + LINT_OPTION + " " + chartPath;
+ if (isStrictLint) {
+ return command + " --strict";
+ }
+ return command;
+ }
+
+ private LintValidationResult runHelmLint(String helmCommand) {
+ BashOutput chartLintResult = executor.execute(helmCommand);
+
+ List<String> lintErrors = parseLintError(chartLintResult.getOutputLines());
+ List<String> lintWarnings = parseWarningError(chartLintResult.getOutputLines());
+
+ boolean isSuccessExitStatus = isSuccessExitStatus(chartLintResult.getExitValue());
+
+ if (isInvalidWithoutStandardError(isSuccessExitStatus, lintErrors, lintWarnings)) {
+ return new LintValidationResult(INVALID_RESULT, chartLintResult.getOutputLines(), new ArrayList<>());
+ }
+
+ return new LintValidationResult(isSuccessExitStatus, lintErrors, lintWarnings);
+ }
+
+ private boolean isInvalidWithoutStandardError(boolean isValid, List<String> lintErrors, List<String> lintWarnings) {
+ return !isValid && lintErrors.isEmpty() && lintWarnings.isEmpty();
+ }
+
+ private boolean isSuccessExitStatus(int exitValue) {
+ return exitValue == 0;
+ }
+
+ private List<String> parseTemplateError(List<String> outputLines) {
+
+ return outputLines.stream()
+ .filter(s -> s.startsWith("Error:"))
+ .collect(Collectors.toList());
+ }
+
+ private List<String> parseWarningError(List<String> outputLines) {
+ return outputLines.stream()
+ .filter(s -> s.startsWith("[WARNING]"))
+ .collect(Collectors.toList());
+ }
+
+ private List<String> parseLintError(List<String> outputLines) {
+ return outputLines.stream()
+ .filter(s -> s.startsWith("[ERROR]") || s.startsWith("Error"))
+ .filter(this::isNotHelmSummaryMessage)
+ .collect(Collectors.toList());
+ }
+
+ private boolean isNotHelmSummaryMessage(String line) {
+ Pattern pattern = Pattern.compile(HELM_SUMMARY_MESSAGE_PATTERN);
+ return !pattern.matcher(line).matches();
+ }
+}