diff --git a/deployment/pom.xml b/deployment/pom.xml index acd8c48..6a03836 100644 --- a/deployment/pom.xml +++ b/deployment/pom.xml @@ -27,19 +27,8 @@ quarkus-kubernetes-deployment - io.dekorate - helm-annotations - noapt - - - io.sundr - * - - - com.sun - tools - - + io.github.yaml-path + yaml-path diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmChartUploader.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmChartUploader.java index 5c6b06d..ee3ca49 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmChartUploader.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmChartUploader.java @@ -1,5 +1,9 @@ package io.quarkiverse.helm.deployment; +import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -14,19 +18,16 @@ import java.net.URL; import java.nio.charset.Charset; -import org.apache.commons.lang3.StringUtils; import org.jboss.logging.Logger; -import io.dekorate.utils.Strings; - public final class HelmChartUploader { - private static Logger LOGGER = Logger.getLogger(HelmProcessor.class); + private static final Logger LOGGER = Logger.getLogger(HelmProcessor.class); - private static String APPLICATION_GZIP = "application/gzip"; + private static final String APPLICATION_GZIP = "application/gzip"; private static final String CONTENT_TYPE = "Content-Type"; - private static String POST = "POST"; - private static String PUT = "PUT"; + private static final String POST = "POST"; + private static final String PUT = "PUT"; private HelmChartUploader() { @@ -61,7 +62,7 @@ static void pushToHelmRepository(File tarball, HelmRepository helmRepository) { } private static void validate(HelmRepository repository) { - if (repository.url().isEmpty() || Strings.isNullOrEmpty(repository.url().get())) { + if (repository.url().isEmpty() || isEmpty(repository.url().get())) { throw new RuntimeException("The push to a Helm repository is enabled (the property `quarkus.helm.repository.push` " + "is true), but the repository URL was not provided (the property `quarkus.helm.repository.url`)."); } @@ -71,8 +72,8 @@ private static void validate(HelmRepository repository) { + "is true), but the repository type was not provided (the property `quarkus.helm.repository.type`)."); } - if ((Strings.isNotNullOrEmpty(repository.getUsername()) && Strings.isNullOrEmpty(repository.getPassword())) - || (Strings.isNotNullOrEmpty(repository.getPassword()) && Strings.isNullOrEmpty(repository.getUsername()))) { + if ((isNotEmpty(repository.getUsername()) && isEmpty(repository.getPassword())) + || (isNotEmpty(repository.getPassword()) && isEmpty(repository.getUsername()))) { throw new RuntimeException("The push to a Helm repository is enabled (the property `quarkus.helm.repository.push` " + "is true), but either the username (the property `quarkus.helm.repository.username`) " + "or the password (the property `quarkus.helm.repository.password`) was not set."); @@ -106,7 +107,7 @@ private static HttpURLConnection deductConnectionByRepositoryType(File tarball, } private static String formatRepositoryURL(File file, HelmRepository repository) { - return String.format("%s%s", StringUtils.appendIfMissing(repository.url().get(), "/"), file.getName()); + return String.format("%s%s", appendIfMissing(repository.url().get(), "/"), file.getName()); } private static HttpURLConnection createConnection(HelmRepository repository, String url) throws IOException { @@ -119,7 +120,7 @@ private static HttpURLConnection createConnection(HelmRepository repository, Str } private static void verifyAndSetAuthentication(HelmRepository helmRepository) { - if (Strings.isNotNullOrEmpty(helmRepository.getUsername()) && Strings.isNotNullOrEmpty(helmRepository.getPassword())) { + if (isNotEmpty(helmRepository.getUsername()) && isNotEmpty(helmRepository.getPassword())) { PasswordAuthentication authentication = new PasswordAuthentication(helmRepository.getUsername(), helmRepository.getPassword().toCharArray()); diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmProcessor.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmProcessor.java index c9d60be..94940b5 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmProcessor.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmProcessor.java @@ -1,18 +1,17 @@ package io.quarkiverse.helm.deployment; -import static io.github.yamlpath.utils.StringUtils.EMPTY; import static io.quarkiverse.helm.deployment.HelmChartUploader.pushToHelmRepository; import static io.quarkiverse.helm.deployment.utils.SystemPropertiesUtils.getPropertyFromSystem; import static io.quarkiverse.helm.deployment.utils.SystemPropertiesUtils.getSystemProperties; import static io.quarkiverse.helm.deployment.utils.SystemPropertiesUtils.hasSystemProperties; import static io.quarkus.deployment.Capability.OPENSHIFT; +import static org.apache.commons.lang3.StringUtils.EMPTY; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -33,16 +32,9 @@ import io.dekorate.ConfigReference; import io.dekorate.Session; -import io.dekorate.helm.config.HelmChartConfigBuilder; -import io.dekorate.helm.config.HelmDependencyBuilder; -import io.dekorate.helm.config.ValuesSchema; -import io.dekorate.helm.config.ValuesSchemaBuilder; -import io.dekorate.helm.config.ValuesSchemaProperty; -import io.dekorate.helm.config.ValuesSchemaPropertyBuilder; import io.dekorate.kubernetes.config.ContainerBuilder; import io.dekorate.kubernetes.decorator.AddInitContainerDecorator; import io.dekorate.project.Project; -import io.dekorate.utils.Strings; import io.quarkiverse.helm.deployment.decorators.LowPriorityAddEnvVarDecorator; import io.quarkiverse.helm.deployment.utils.HelmConfigUtils; import io.quarkus.deployment.Capabilities; @@ -184,10 +176,6 @@ private void doGenerateResources(ApplicationInfoBuildItem app, OutputTargetBuild final Map> deploymentTargets = toDeploymentTargets(dekorateOutput.getGeneratedFiles(), generatedResources); - // Config - io.dekorate.helm.config.HelmChartConfig dekorateHelmChartConfig = toDekorateHelmChartConfig(app, config); - List valueReferencesFromUser = toValueReferences(config); - // Deduct deployment target to push String deploymentTargetToPush = deductDeploymentTarget(config, deploymentTargets); @@ -197,14 +185,14 @@ private void doGenerateResources(ApplicationInfoBuildItem app, OutputTargetBuild Path chartOutputFolder = outputFolder.resolve(deploymentTarget); deleteOutputHelmFolderIfExists(chartOutputFolder); - Map generated = helmWriter.writeHelmFiles(project, - dekorateHelmChartConfig, - valueReferencesFromUser, + Map generated = helmWriter.writeHelmFiles( + config.name().orElse(app.getName()), + project, + config, getConfigReferencesFromSession(deploymentTarget, dekorateOutput), inputFolder, chartOutputFolder, - filesInDeploymentTarget.getValue(), - config.valuesProfileSeparator()); + filesInDeploymentTarget.getValue()); // Push to Helm repository if enabled if (config.repository().push() && deploymentTargetToPush.equals(deploymentTarget)) { @@ -359,125 +347,6 @@ private Map> toDeploymentTargets(List generatedFiles, return filesByDeploymentTarget; } - private io.dekorate.helm.config.HelmChartConfig toDekorateHelmChartConfig(ApplicationInfoBuildItem app, - HelmChartConfig config) { - HelmChartConfigBuilder builder = new HelmChartConfigBuilder() - .withEnabled(config.enabled()) - .withApiVersion(config.apiVersion()) - .withName(config.name().orElse(app.getName())) - .withCreateTarFile(config.createTarFile() || config.repository().push()) - .withCreateValuesSchemaFile(config.createValuesSchemaFile()) - .withCreateReadmeFile(config.createReadmeFile()) - .withVersion(config.version().orElse(app.getVersion())) - .withExtension(config.extension()) - .withValuesRootAlias(config.valuesRootAlias()) - .withNotes(config.notes()); - config.description().ifPresent(builder::withDescription); - config.keywords().ifPresent(builder::addAllToKeywords); - config.icon().ifPresent(builder::withIcon); - config.condition().ifPresent(builder::withCondition); - config.tags().ifPresent(builder::withTags); - config.appVersion().ifPresent(builder::withAppVersion); - config.deprecated().ifPresent(builder::withDeprecated); - config.annotations().entrySet().forEach(e -> builder.addNewAnnotation(e.getKey(), e.getValue())); - config.kubeVersion().ifPresent(builder::withKubeVersion); - config.type().ifPresent(builder::withType); - config.home().ifPresent(builder::withHome); - config.sources().ifPresent(builder::addAllToSources); - config.maintainers().entrySet() - .forEach(e -> builder.addNewMaintainer( - defaultString(e.getValue().name(), e.getKey()), - defaultString(e.getValue().email()), - defaultString(e.getValue().url()))); - config.dependencies().entrySet() - .forEach(e -> builder.addToDependencies(toDekorateHelmDependencyConfig(e.getKey(), e.getValue()))); - config.tarFileClassifier().ifPresent(builder::withTarFileClassifier); - config.expressions().values().forEach(e -> builder.addNewExpression(e.path(), e.expression())); - config.addIfStatement().entrySet() - .forEach(e -> { - builder.addNewAddIfStatement( - defaultString(e.getValue().property(), e.getKey()), - defaultString(e.getValue().onResourceKind()), - defaultString(e.getValue().onResourceName()), - e.getValue().withDefaultValue(), - e.getValue().description()); - }); - - builder.withValuesSchema(toValuesSchema(config.valuesSchema())); - - return builder.build(); - } - - private ValuesSchema toValuesSchema(ValuesSchemaConfig valuesSchema) { - List properties = new ArrayList<>(); - for (Map.Entry property : valuesSchema.properties().entrySet()) { - String name = property.getValue().name().orElse(property.getKey()); - - properties.add(new ValuesSchemaPropertyBuilder() - .withName(name) - .withType(property.getValue().type()) - .withDescription(defaultString(property.getValue().description())) - .withMaximum(property.getValue().maximum().orElse(Integer.MAX_VALUE)) - .withMinimum(property.getValue().minimum().orElse(Integer.MIN_VALUE)) - .withRequired(property.getValue().required()) - .withPattern(defaultString(property.getValue().pattern())) - .build()); - } - - return new ValuesSchemaBuilder() - .withTitle(valuesSchema.title()) - .withProperties(properties.toArray(new ValuesSchemaProperty[0])) - .build(); - } - - private io.dekorate.helm.config.HelmDependency toDekorateHelmDependencyConfig(String dependencyName, - HelmDependencyConfig dependency) { - HelmDependencyBuilder builder = new HelmDependencyBuilder() - .withName(defaultString(dependency.name(), dependencyName)) - .withAlias(defaultString(dependency.alias(), defaultString(dependency.name(), dependencyName))) - .withVersion(dependency.version()) - .withRepository(dependency.repository()) - .withCondition(defaultString(dependency.condition())) - .withTags(defaultArray(dependency.tags())) - .withEnabled(dependency.enabled().orElse(true)); - - return builder.build(); - } - - private List toValueReferences(HelmChartConfig config) { - return config.values().entrySet().stream() - .map(e -> new ConfigReference.Builder(defaultString(e.getValue().property(), e.getKey()), - defaultArray(e.getValue().paths())) - .withValue(toValue(e.getValue())) - .withDescription(defaultString(e.getValue().description(), EMPTY)) - .withExpression(defaultString(e.getValue().expression())) - .withProfile(defaultString(e.getValue().profile())) - .withRequired(e.getValue().required()) - .withPattern(defaultString(e.getValue().pattern())) - .withMaximum(e.getValue().maximum().orElse(Integer.MAX_VALUE)) - .withMinimum(e.getValue().minimum().orElse(Integer.MIN_VALUE)) - .build()) - .collect(Collectors.toList()); - } - - private Object toValue(ValueReferenceConfig v) { - if (v.valueAsInt().isPresent()) { - return v.valueAsInt().get(); - } else if (v.valueAsBool().isPresent()) { - return v.valueAsBool().get(); - } else if (!v.valueAsMap().isEmpty()) { - return v.valueAsMap(); - } else if (v.valueAsList().isPresent()) { - return v.valueAsList().get(); - } - - return v.value().orElse(null); - } - - private String defaultString(Optional value) { - return defaultString(value, null); - } - private String defaultString(Optional value, String defaultStr) { if (value.isEmpty() || StringUtils.isEmpty(value.get())) { return defaultStr; @@ -486,10 +355,6 @@ private String defaultString(Optional value, String defaultStr) { return value.get(); } - private static String[] defaultArray(Optional> optional) { - return optional.map(l -> l.toArray(new String[0])).orElse(new String[0]); - } - private String mapProperty(String deploymentName, BuildProducer decorators, String property, Map propertiesFromConfigSource) { if (!hasSystemProperties(property)) { @@ -542,7 +407,7 @@ public static String getDeploymentName(Capabilities capabilities, ApplicationInf } private boolean isPropertiesConfigSource(String sourceName) { - return Strings.isNotNullOrEmpty(sourceName) && sourceName.startsWith(PROPERTIES_CONFIG_SOURCE); + return StringUtils.isNotEmpty(sourceName) && sourceName.startsWith(PROPERTIES_CONFIG_SOURCE); } private boolean isBuildTimeProperty(String name) { diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmRepository.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmRepository.java index 7930456..95abf99 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmRepository.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/HelmRepository.java @@ -2,7 +2,8 @@ import java.util.Optional; -import io.dekorate.utils.Strings; +import org.apache.commons.lang3.StringUtils; + import io.smallrye.config.WithDefault; public interface HelmRepository { @@ -39,10 +40,10 @@ public interface HelmRepository { Optional password(); default String getUsername() { - return username().filter(Strings::isNotNullOrEmpty).orElse(null); + return username().filter(StringUtils::isNotEmpty).orElse(null); } default String getPassword() { - return password().filter(Strings::isNotNullOrEmpty).orElse(null); + return password().filter(StringUtils::isNotEmpty).orElse(null); } } diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/QuarkusHelmWriterSessionListener.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/QuarkusHelmWriterSessionListener.java index 752537d..dd4438d 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/QuarkusHelmWriterSessionListener.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/QuarkusHelmWriterSessionListener.java @@ -1,24 +1,7 @@ -/** - * Copyright 2018 The original authors. - * - * 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. - * - **/ package io.quarkiverse.helm.deployment; -import static io.dekorate.helm.util.HelmTarArchiver.createTarBall; -import static io.dekorate.utils.Strings.isNullOrEmpty; import static io.quarkiverse.helm.deployment.utils.HelmConfigUtils.deductProperty; +import static io.quarkiverse.helm.deployment.utils.HelmTarArchiver.createTarBall; import static io.quarkiverse.helm.deployment.utils.MapUtils.toMultiValueUnsortedMap; import static io.quarkiverse.helm.deployment.utils.MapUtils.toPlainMap; import static io.quarkiverse.helm.deployment.utils.ValuesSchemaUtils.createSchema; @@ -33,6 +16,7 @@ import static io.quarkiverse.helm.deployment.utils.YamlExpressionParserUtils.readAndSet; import static io.quarkiverse.helm.deployment.utils.YamlExpressionParserUtils.set; import static io.quarkiverse.helm.deployment.utils.YamlExpressionParserUtils.toExpression; +import static org.apache.commons.lang3.ObjectUtils.isEmpty; import java.io.ByteArrayOutputStream; import java.io.File; @@ -56,28 +40,24 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; + import com.fasterxml.jackson.core.type.TypeReference; import io.dekorate.ConfigReference; import io.dekorate.Logger; import io.dekorate.LoggerFactory; -import io.dekorate.helm.config.AddIfStatement; -import io.dekorate.helm.config.Annotation; -import io.dekorate.helm.config.HelmChartConfig; -import io.dekorate.helm.config.HelmExpression; -import io.dekorate.helm.listener.HelmWriterSessionListener; -import io.dekorate.helm.model.Chart; -import io.dekorate.helm.model.HelmDependency; -import io.dekorate.helm.model.Maintainer; -import io.dekorate.helm.util.HelmConfigUtils; -import io.dekorate.helm.util.MapUtils; import io.dekorate.project.Project; import io.dekorate.utils.Exec; import io.dekorate.utils.Maps; import io.dekorate.utils.Serialization; -import io.dekorate.utils.Strings; import io.github.yamlpath.YamlExpressionParser; import io.github.yamlpath.YamlPath; +import io.quarkiverse.helm.deployment.model.Chart; +import io.quarkiverse.helm.deployment.model.HelmDependency; +import io.quarkiverse.helm.deployment.model.Maintainer; +import io.quarkiverse.helm.deployment.utils.FileUtils; +import io.quarkiverse.helm.deployment.utils.MapUtils; import io.quarkiverse.helm.deployment.utils.ReadmeBuilder; import io.quarkiverse.helm.deployment.utils.ValuesHolder; @@ -109,36 +89,34 @@ public class QuarkusHelmWriterSessionListener { * * @return the list of the Helm generated files. */ - public Map writeHelmFiles(Project project, - io.dekorate.helm.config.HelmChartConfig helmConfig, - List valueReferencesFromUser, + public Map writeHelmFiles(String name, + Project project, + HelmChartConfig helmConfig, List valueReferencesFromDecorators, Path inputDir, Path outputDir, - Collection generatedFiles, - String valuesProfileSeparator) { + Collection generatedFiles) { Map artifacts = new HashMap<>(); - if (helmConfig.isEnabled()) { - validateHelmConfig(helmConfig); + if (helmConfig.enabled()) { try { - LOGGER.info(String.format("Creating Helm Chart \"%s\"", helmConfig.getName())); + LOGGER.info(String.format("Creating Helm Chart \"%s\"", name)); ValuesHolder values = populateValuesFromConfig(helmConfig, inputDir); List> resources = populateValuesFromConfigReferences(helmConfig, generatedFiles, values, - valueReferencesFromUser, valueReferencesFromDecorators); - artifacts.putAll(processTemplates(helmConfig, helmConfig.getAddIfStatements(), inputDir, outputDir, resources)); - artifacts.putAll(createChartYaml(helmConfig, project, inputDir, outputDir)); - artifacts.putAll(createValuesYaml(helmConfig, inputDir, outputDir, values, valuesProfileSeparator)); + valueReferencesFromDecorators); + artifacts.putAll(processTemplates(name, helmConfig, inputDir, outputDir, resources)); + artifacts.putAll(createChartYaml(name, helmConfig, project, inputDir, outputDir)); + artifacts.putAll(createValuesYaml(name, helmConfig, inputDir, outputDir, values)); // To follow Helm file structure standards: - artifacts.putAll(createEmptyChartFolder(helmConfig, outputDir)); - artifacts.putAll(addNotesIntoTemplatesFolder(helmConfig, inputDir, outputDir)); - artifacts.putAll(addAdditionalResources(helmConfig, inputDir, outputDir)); + artifacts.putAll(createEmptyChartFolder(name, outputDir)); + artifacts.putAll(addNotesIntoTemplatesFolder(name, helmConfig, inputDir, outputDir)); + artifacts.putAll(addAdditionalResources(name, inputDir, outputDir)); // Final step: packaging - if (helmConfig.isCreateTarFile()) { - fetchDependencies(helmConfig, outputDir); - artifacts.putAll(createTarball(helmConfig, project, outputDir, artifacts)); + if (helmConfig.createTarFile()) { + fetchDependencies(name, helmConfig, outputDir); + artifacts.putAll(createTarball(name, helmConfig, project, outputDir, artifacts)); } } catch (IOException e) { @@ -149,7 +127,7 @@ public Map writeHelmFiles(Project project, return artifacts; } - private Map addAdditionalResources(HelmChartConfig helmConfig, Path inputDir, Path outputDir) + private Map addAdditionalResources(String name, Path inputDir, Path outputDir) throws IOException { if (inputDir == null || !inputDir.toFile().exists()) { return Collections.emptyMap(); @@ -158,20 +136,20 @@ private Map addAdditionalResources(HelmChartConfig helmConfig, P Map artifacts = new HashMap<>(); for (File source : inputDir.toFile().listFiles()) { if (ADDITIONAL_CHART_FILES.stream().anyMatch(source.getName()::equalsIgnoreCase)) { - artifacts.putAll(addAdditionalResource(helmConfig, outputDir, source)); + artifacts.putAll(addAdditionalResource(name, outputDir, source)); } } return artifacts; } - private Map addAdditionalResource(HelmChartConfig helmConfig, Path outputDir, File source) + private Map addAdditionalResource(String name, Path outputDir, File source) throws IOException { if (!source.exists()) { return Collections.emptyMap(); } - Path destination = getChartOutputDir(helmConfig, outputDir).resolve(source.getName()); + Path destination = getChartOutputDir(name, outputDir).resolve(source.getName()); if (source.isDirectory()) { Files.createDirectory(destination); for (File file : source.listFiles()) { @@ -184,9 +162,9 @@ private Map addAdditionalResource(HelmChartConfig helmConfig, Pa return Collections.singletonMap(destination.toString(), EMPTY); } - private void fetchDependencies(io.dekorate.helm.config.HelmChartConfig helmConfig, Path outputDir) { - if (helmConfig.getDependencies() != null && helmConfig.getDependencies().length > 0) { - Path chartFolder = getChartOutputDir(helmConfig, outputDir); + private void fetchDependencies(String name, HelmChartConfig helmConfig, Path outputDir) { + if (helmConfig.dependencies() != null && !helmConfig.dependencies().isEmpty()) { + Path chartFolder = getChartOutputDir(name, outputDir); ByteArrayOutputStream out = new ByteArrayOutputStream(); boolean success = Exec.inPath(chartFolder) .redirectingOutput(out) @@ -200,13 +178,7 @@ private void fetchDependencies(io.dekorate.helm.config.HelmChartConfig helmConfi } } - private void validateHelmConfig(io.dekorate.helm.config.HelmChartConfig helmConfig) { - if (isNullOrEmpty(helmConfig.getName())) { - throw new RuntimeException("Helm Chart name is required!"); - } - } - - private Map addNotesIntoTemplatesFolder(io.dekorate.helm.config.HelmChartConfig helmConfig, Path inputDir, + private Map addNotesIntoTemplatesFolder(String name, HelmChartConfig helmConfig, Path inputDir, Path outputDir) throws IOException { InputStream notesInputStream; @@ -215,41 +187,35 @@ private Map addNotesIntoTemplatesFolder(io.dekorate.helm.config. if (notesInInputDir.exists()) { notesInputStream = new FileInputStream(notesInInputDir); } else { - if (isNullOrEmpty(helmConfig.getNotes())) { + if (isEmpty(helmConfig.notes())) { return Collections.emptyMap(); } - notesInputStream = getResourceFromClasspath(helmConfig.getNotes()); + notesInputStream = getResourceFromClasspath(helmConfig.notes()); } if (notesInputStream == null) { - throw new RuntimeException("Could not find the notes template file in the classpath at " + helmConfig.getNotes()); + throw new RuntimeException("Could not find the notes template file in the classpath at " + helmConfig.notes()); } - Path chartOutputDir = getChartOutputDir(helmConfig, outputDir).resolve(TEMPLATES).resolve(NOTES); + Path chartOutputDir = getChartOutputDir(name, outputDir).resolve(TEMPLATES).resolve(NOTES); Files.copy(notesInputStream, chartOutputDir); return Collections.singletonMap(chartOutputDir.toString(), EMPTY); } private InputStream getResourceFromClasspath(String notes) { // Try to locate the file from the context class loader - InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(notes); - if (is == null) { - // if not found, try to find it in the current classpath. - is = HelmWriterSessionListener.class.getResourceAsStream(notes); - } - - return is; + return Thread.currentThread().getContextClassLoader().getResourceAsStream(notes); } - private Map createEmptyChartFolder(io.dekorate.helm.config.HelmChartConfig helmConfig, Path outputDir) + private Map createEmptyChartFolder(String name, Path outputDir) throws IOException { - Path emptyChartsDir = getChartOutputDir(helmConfig, outputDir).resolve(CHARTS); + Path emptyChartsDir = getChartOutputDir(name, outputDir).resolve(CHARTS); Files.createDirectories(emptyChartsDir); return Collections.singletonMap(emptyChartsDir.toString(), EMPTY); } - private Map createValuesYaml(io.dekorate.helm.config.HelmChartConfig helmConfig, - Path inputDir, Path outputDir, ValuesHolder valuesHolder, String valuesProfileSeparator) + private Map createValuesYaml(String name, HelmChartConfig helmConfig, + Path inputDir, Path outputDir, ValuesHolder valuesHolder) throws IOException { Map prodValues = valuesHolder.getProdValues(); Map> valuesByProfile = valuesHolder.getValuesByProfile(); @@ -269,30 +235,30 @@ private Map createValuesYaml(io.dekorate.helm.config.HelmChartCo // Create the values..yaml file artifacts.putAll(writeFileAsYaml(mergeWithFileIfExists(inputDir, VALUES + YAML, toValuesMap(values)), - getChartOutputDir(helmConfig, outputDir) - .resolve(VALUES + valuesProfileSeparator + profile + YAML))); + getChartOutputDir(name, outputDir) + .resolve(VALUES + helmConfig.valuesProfileSeparator() + profile + YAML))); } // Next, we process the prod profile artifacts.putAll(writeFileAsYaml(mergeWithFileIfExists(inputDir, VALUES + YAML, toValuesMap(prodValues)), - getChartOutputDir(helmConfig, outputDir).resolve(VALUES + YAML))); + getChartOutputDir(name, outputDir).resolve(VALUES + YAML))); // Next, the "values.schema.json" file - if (helmConfig.isCreateValuesSchemaFile()) { + if (helmConfig.createValuesSchemaFile()) { Map schemaAsMap = createSchema(helmConfig, prodValues); artifacts.putAll( writeFileAsJson(mergeWithFileIfExists(inputDir, VALUES_SCHEMA, MapUtils.toMultiValueSortedMap(schemaAsMap)), - getChartOutputDir(helmConfig, outputDir).resolve(VALUES_SCHEMA))); + getChartOutputDir(name, outputDir).resolve(VALUES_SCHEMA))); } else { - artifacts.putAll(addAdditionalResource(helmConfig, outputDir, inputDir.resolve(VALUES_SCHEMA).toFile())); + artifacts.putAll(addAdditionalResource(name, outputDir, inputDir.resolve(VALUES_SCHEMA).toFile())); } // Next, the "README.md" file - if (helmConfig.isCreateReadmeFile()) { - String readmeContent = ReadmeBuilder.build(helmConfig, prodValues); - artifacts.putAll(writeFile(readmeContent, getChartOutputDir(helmConfig, outputDir).resolve(README))); + if (helmConfig.createReadmeFile()) { + String readmeContent = ReadmeBuilder.build(name, helmConfig, prodValues); + artifacts.putAll(writeFile(readmeContent, getChartOutputDir(name, outputDir).resolve(README))); } else { - artifacts.putAll(addAdditionalResource(helmConfig, outputDir, inputDir.resolve(README).toFile())); + artifacts.putAll(addAdditionalResource(name, outputDir, inputDir.resolve(README).toFile())); } return artifacts; @@ -325,20 +291,20 @@ private Map mergeWithFileIfExists(Path inputDir, String file, Ma return valuesAsMultiValueMap; } - private Map createTarball(io.dekorate.helm.config.HelmChartConfig helmConfig, Project project, + private Map createTarball(String name, HelmChartConfig helmConfig, Project project, Path outputDir, Map artifacts) throws IOException { File tarballFile = outputDir.resolve(String.format("%s-%s%s.%s", - helmConfig.getName(), + name, getVersion(helmConfig, project), - isNullOrEmpty(helmConfig.getTarFileClassifier()) ? EMPTY : "-" + helmConfig.getTarFileClassifier(), - helmConfig.getExtension())) + helmConfig.tarFileClassifier().map(c -> "-" + c).orElse(EMPTY), + helmConfig.extension())) .toFile(); LOGGER.debug(String.format("Creating Helm configuration Tarball: '%s'", tarballFile)); - Path helmSources = getChartOutputDir(helmConfig, outputDir); + Path helmSources = getChartOutputDir(name, outputDir); List files = new ArrayList<>(); for (String filePath : artifacts.keySet()) { @@ -350,39 +316,34 @@ private Map createTarball(io.dekorate.helm.config.HelmChartConfi } } - createTarBall(tarballFile, helmSources.toFile(), files, helmConfig.getExtension(), - tae -> tae.setName(String.format("%s/%s", helmConfig.getName(), tae.getName()))); + createTarBall(tarballFile, helmSources.toFile(), files, helmConfig.extension(), + tae -> tae.setName(String.format("%s/%s", name, tae.getName()))); return Collections.singletonMap(tarballFile.toString(), null); } - private String getVersion(io.dekorate.helm.config.HelmChartConfig helmConfig, Project project) { - if (isNullOrEmpty(helmConfig.getVersion())) { - return project.getBuildInfo().getVersion(); - } - - return helmConfig.getVersion(); + private String getVersion(HelmChartConfig helmConfig, Project project) { + return helmConfig.version().orElse(project.getBuildInfo().getVersion()); } - private Map processTemplates(io.dekorate.helm.config.HelmChartConfig helmConfig, - AddIfStatement[] addIfStatements, + private Map processTemplates(String name, HelmChartConfig helmConfig, Path inputDir, Path outputDir, List> resources) throws IOException { Map templates = new HashMap<>(); - Path templatesDir = getChartOutputDir(helmConfig, outputDir).resolve(TEMPLATES); + Path templatesDir = getChartOutputDir(name, outputDir).resolve(TEMPLATES); Files.createDirectories(templatesDir); Map functionsByResource = processUserDefinedTemplates(inputDir, templates, templatesDir); // Split yamls in separated files by kind for (Map resource : resources) { // Add user defined expressions - if (helmConfig.getExpressions() != null) { + if (helmConfig.expressions() != null) { YamlExpressionParser parser = new YamlExpressionParser(Arrays.asList(resource)); - for (HelmExpression expressionConfig : helmConfig.getExpressions()) { - if (expressionConfig.getPath() != null && expressionConfig.getExpression() != null) { - readAndSet(parser, expressionConfig.getPath(), expressionConfig.getExpression()); + for (ExpressionConfig expressionConfig : helmConfig.expressions().values()) { + if (expressionConfig.path() != null && expressionConfig.expression() != null) { + readAndSet(parser, expressionConfig.path(), expressionConfig.expression()); } } } @@ -398,12 +359,14 @@ private Map processTemplates(io.dekorate.helm.config.HelmChartCo } // Add if statements at resource level - for (AddIfStatement addIfStatement : addIfStatements) { - if ((isNullOrEmpty(addIfStatement.getOnResourceKind()) || addIfStatement.getOnResourceKind().equals(kind)) - && (isNullOrEmpty(addIfStatement.getOnResourceName()) - || addIfStatement.getOnResourceName().equals(getNameFromResource(resource)))) { - - String property = deductProperty(helmConfig, addIfStatement.getProperty()); + for (Map.Entry addIfStatement : helmConfig.addIfStatement().entrySet()) { + AddIfStatementConfig addIfStatementConfig = addIfStatement.getValue(); + if ((addIfStatementConfig.onResourceKind().isEmpty() + || addIfStatementConfig.onResourceKind().get().equals(kind)) + && (addIfStatementConfig.onResourceName().isEmpty() + || addIfStatementConfig.onResourceName().get().equals(getNameFromResource(resource)))) { + String propertyName = addIfStatementConfig.property().orElse(addIfStatement.getKey()); + String property = deductProperty(helmConfig, propertyName); adaptedString = String.format(IF_STATEMENT_START_TAG, property) + System.lineSeparator() @@ -459,7 +422,7 @@ private Map processUserDefinedTemplates(Path inputDir, Map processUserDefinedTemplates(Path inputDir, Map dependency : helmConfig.dependencies().entrySet()) { + dependency.getValue().condition().ifPresent(condition -> { + String dependencyName = dependency.getValue().name().orElse(dependency.getKey()); + String propertyName = deductProperty(helmConfig, condition); ConfigReference configReference = new ConfigReference.Builder(propertyName, new String[0]) - .withDescription("Flag to enable/disable the dependency '" + dependency.getName() + "'") + .withDescription("Flag to enable/disable the dependency '" + dependencyName + "'") .build(); values.put(propertyName, configReference, true); - } + }); } // Populate if statements expressions - for (AddIfStatement addIfStatement : helmConfig.getAddIfStatements()) { - String propertyName = deductProperty(helmConfig, addIfStatement.getProperty()); + for (Map.Entry addIfStatement : helmConfig.addIfStatement().entrySet()) { + String property = addIfStatement.getValue().property().orElse(addIfStatement.getKey()); + String propertyName = deductProperty(helmConfig, property); ConfigReference configReference = new ConfigReference.Builder(propertyName, new String[0]) - .withDescription(addIfStatement.getDescription()) - .withValue(addIfStatement.getWithDefaultValue()) + .withDescription(addIfStatement.getValue().description()) + .withValue(addIfStatement.getValue().withDefaultValue()) .build(); values.put(propertyName, configReference); } @@ -519,11 +484,24 @@ private ValuesHolder populateValuesFromConfig(io.dekorate.helm.config.HelmChartC return values; } - private List> populateValuesFromConfigReferences(io.dekorate.helm.config.HelmChartConfig helmConfig, + private List> populateValuesFromConfigReferences(HelmChartConfig helmConfig, Collection generatedFiles, ValuesHolder values, - List valuesReferencesFromUser, List valuesReferencesFromDecorators) throws IOException { + List valuesReferencesFromUser = helmConfig.values().entrySet().stream() + .map(e -> new ConfigReference.Builder(e.getValue().property().orElse(e.getKey()), + e.getValue().paths().map(l -> l.toArray(new String[0])).orElse(new String[0])) + .withValue(toValue(e.getValue())) + .withDescription(e.getValue().description().orElse(EMPTY)) + .withExpression(e.getValue().expression().orElse(null)) + .withProfile(e.getValue().profile().orElse(null)) + .withRequired(e.getValue().required()) + .withPattern(e.getValue().pattern().orElse(null)) + .withMaximum(e.getValue().maximum().orElse(Integer.MAX_VALUE)) + .withMinimum(e.getValue().minimum().orElse(Integer.MIN_VALUE)) + .build()) + .collect(Collectors.toList()); + List> allResources = new LinkedList<>(); for (File generatedFile : generatedFiles) { if (!generatedFile.getName().toLowerCase().matches(YAML_REG_EXP)) { @@ -618,55 +596,54 @@ private void processValueReference(String property, Object value, ConfigReferenc if (actualValue != null) { set(parser, path, toExpression(property, value, found, valueReference)); values.putIfAbsent(property, valueReference, actualValue, profile); - if (isNullOrEmpty(profile)) { + if (StringUtils.isEmpty(profile)) { seen.putIfAbsent(property, actualValue); } } } } else { values.putIfAbsent(property, valueReference, value, profile); - if (isNullOrEmpty(profile)) { + if (StringUtils.isEmpty(profile)) { seen.putIfAbsent(property, value); } } } - private Map createChartYaml(io.dekorate.helm.config.HelmChartConfig helmConfig, Project project, + private Map createChartYaml(String name, HelmChartConfig helmConfig, Project project, Path inputDir, Path outputDir) throws IOException { final Chart chart = new Chart(); - chart.setName(helmConfig.getName()); + chart.setName(name); chart.setVersion(getVersion(helmConfig, project)); - chart.setDescription(helmConfig.getDescription()); - chart.setHome(helmConfig.getHome()); - chart.setSources(Arrays.asList(helmConfig.getSources())); - chart.setMaintainers(Arrays.stream(helmConfig.getMaintainers()) - .map(m -> new Maintainer(m.getName(), m.getEmail(), m.getUrl())) + helmConfig.description().ifPresent(chart::setDescription); + helmConfig.home().ifPresent(chart::setHome); + helmConfig.sources().ifPresent(chart::setSources); + chart.setMaintainers(helmConfig.maintainers().entrySet() + .stream() + .map(e -> new Maintainer(e.getValue().name().orElse(e.getKey()), e.getValue().email().orElse(EMPTY), + e.getValue().url().orElse(EMPTY))) .collect(Collectors.toList())); - chart.setIcon(helmConfig.getIcon()); - chart.setApiVersion(helmConfig.getApiVersion()); - chart.setCondition(helmConfig.getCondition()); - chart.setTags(helmConfig.getTags()); - chart.setAppVersion(helmConfig.getAppVersion()); - if (helmConfig.isDeprecated()) { - chart.setDeprecated(helmConfig.isDeprecated()); - } - chart.setAnnotations(Arrays.stream(helmConfig.getAnnotations()) - .collect(Collectors.toMap(Annotation::getKey, Annotation::getValue))); - chart.setKubeVersion(helmConfig.getKubeVersion()); - chart.setKeywords(Arrays.asList(helmConfig.getKeywords())); - chart.setDependencies(Arrays.stream(helmConfig.getDependencies()) - .map(d -> new HelmDependency(d.getName(), - Strings.defaultIfEmpty(d.getAlias(), d.getName()), - d.getVersion(), - d.getRepository(), - d.getCondition(), - d.getTags(), - d.isEnabled())) + helmConfig.icon().ifPresent(chart::setIcon); + chart.setApiVersion(helmConfig.apiVersion()); + helmConfig.condition().ifPresent(chart::setCondition); + helmConfig.tags().ifPresent(chart::setTags); + helmConfig.appVersion().ifPresent(chart::setAppVersion); + helmConfig.deprecated().ifPresent(chart::setDeprecated); + chart.setAnnotations(helmConfig.annotations()); + helmConfig.kubeVersion().ifPresent(chart::setKubeVersion); + helmConfig.keywords().ifPresent(chart::setKeywords); + chart.setDependencies(helmConfig.dependencies().entrySet().stream() + .map(d -> new HelmDependency(d.getValue().name().orElse(d.getKey()), + d.getValue().alias().orElse(d.getValue().name().orElse(d.getKey())), + d.getValue().version(), + d.getValue().repository(), + d.getValue().condition().orElse(EMPTY), + d.getValue().tags().map(l -> l.toArray(new String[0])).orElse(new String[0]), + d.getValue().enabled().orElse(true))) .collect(Collectors.toList())); - chart.setType(helmConfig.getType()); + helmConfig.type().ifPresent(chart::setType); - Path yml = getChartOutputDir(helmConfig, outputDir).resolve(CHART_FILENAME).normalize(); + Path yml = getChartOutputDir(name, outputDir).resolve(CHART_FILENAME).normalize(); File userChartFile = inputDir.resolve(CHART_FILENAME).toFile(); Object chartContent = chart; if (userChartFile.exists()) { @@ -694,7 +671,21 @@ private Map writeFile(String value, Path file) throws IOExceptio } } - private Path getChartOutputDir(HelmChartConfig helmConfig, Path outputDir) { - return outputDir.resolve(helmConfig.getName()); + private Path getChartOutputDir(String name, Path outputDir) { + return outputDir.resolve(name); + } + + private Object toValue(ValueReferenceConfig v) { + if (v.valueAsInt().isPresent()) { + return v.valueAsInt().get(); + } else if (v.valueAsBool().isPresent()) { + return v.valueAsBool().get(); + } else if (!v.valueAsMap().isEmpty()) { + return v.valueAsMap(); + } else if (v.valueAsList().isPresent()) { + return v.valueAsList().get(); + } + + return v.value().orElse(null); } } diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/model/Chart.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/Chart.java new file mode 100644 index 0000000..be0a2fe --- /dev/null +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/Chart.java @@ -0,0 +1,199 @@ +package io.quarkiverse.helm.deployment.model; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents the Helm + * Chart.yaml file + */ +@JsonInclude(NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Chart { + @JsonProperty + private String name; + @JsonProperty + private String home; + @JsonProperty + private List sources; + @JsonProperty + private String version; + @JsonProperty + private String description; + @JsonProperty + private List keywords; + @JsonProperty + private List maintainers; + @JsonProperty + private String icon; + @JsonProperty + private String apiVersion; + @JsonProperty + private String condition; + @JsonProperty + private String tags; + @JsonProperty + private String appVersion; + @JsonProperty + private Boolean deprecated; + @JsonProperty + private Map annotations; + @JsonProperty + private String kubeVersion; + @JsonProperty + private List dependencies; + @JsonProperty + private String type; + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getDependencies() { + return dependencies; + } + + public void setDependencies(List dependencies) { + this.dependencies = dependencies; + } + + public List getMaintainers() { + return maintainers; + } + + public void setMaintainers(List maintainers) { + this.maintainers = maintainers; + } + + public String getApiVersion() { + return apiVersion; + } + + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + public List getKeywords() { + return keywords; + } + + public String getCondition() { + return condition; + } + + public void setCondition(String condition) { + this.condition = condition; + } + + public String getTags() { + return tags; + } + + public void setTags(String tags) { + this.tags = tags; + } + + public void setKeywords(List keywords) { + this.keywords = keywords; + } + + public List getSources() { + return sources; + } + + public void setSources(List sources) { + this.sources = sources; + } + + public String getHome() { + return home; + } + + public void setHome(String home) { + this.home = home; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getAppVersion() { + return appVersion; + } + + public void setAppVersion(String appVersion) { + this.appVersion = appVersion; + } + + public Boolean getDeprecated() { + return deprecated; + } + + public void setDeprecated(Boolean deprecated) { + this.deprecated = deprecated; + } + + public Map getAnnotations() { + return annotations; + } + + public void setAnnotations(Map annotations) { + this.annotations = annotations; + } + + public String getKubeVersion() { + return kubeVersion; + } + + public void setKubeVersion(String kubeVersion) { + this.kubeVersion = kubeVersion; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return "Chart{" + + "name='" + name + '\'' + + ", home='" + home + '\'' + + ", version='" + version + '\'' + + '}'; + } + +} diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/model/HelmDependency.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/HelmDependency.java new file mode 100644 index 0000000..f0e182f --- /dev/null +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/HelmDependency.java @@ -0,0 +1,110 @@ +package io.quarkiverse.helm.deployment.model; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents the Helm + * Dependency object + */ +@JsonInclude(NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown = true) +public class HelmDependency { + + @JsonProperty + private String name; + + @JsonProperty + private String version; + + @JsonProperty + private String repository; + + @JsonProperty + private String condition; + + @JsonProperty + private String[] tags; + + @JsonProperty + private Boolean enabled; + + @JsonProperty + private String alias; + + public HelmDependency() { + + } + + public HelmDependency(String name, String alias, String version, String repository, String condition, String[] tags, + Boolean enabled) { + this.name = name; + this.alias = alias; + this.version = version; + this.repository = repository; + this.condition = condition; + this.tags = tags; + if (!enabled) { + this.enabled = false; + } + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getRepository() { + return repository; + } + + public void setRepository(String repository) { + this.repository = repository; + } + + public String getCondition() { + return condition; + } + + public void setCondition(String condition) { + this.condition = condition; + } + + public String[] getTags() { + return tags; + } + + public void setTags(String[] tags) { + this.tags = tags; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } +} diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/model/Maintainer.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/Maintainer.java new file mode 100644 index 0000000..f0ba918 --- /dev/null +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/Maintainer.java @@ -0,0 +1,63 @@ +package io.quarkiverse.helm.deployment.model; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; + +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents the Helm + * Maintainer object + */ +@JsonInclude(NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Maintainer implements Serializable { + + private static final long serialVersionUID = -968020668786188166L; + + @JsonProperty + private String name; + + @JsonProperty + private String email; + + @JsonProperty + private String url; + + public Maintainer() { + + } + + public Maintainer(String name, String email, String url) { + this.name = name; + this.email = email; + this.url = url; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/model/ValuesSchema.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/ValuesSchema.java new file mode 100644 index 0000000..1ed815b --- /dev/null +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/ValuesSchema.java @@ -0,0 +1,71 @@ +package io.quarkiverse.helm.deployment.model; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents the Values Schema json file. + * More information in here + */ +@JsonInclude(NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ValuesSchema { + @JsonProperty(value = "$schema") + private String schema = "https://json-schema.org/draft-07/schema#"; + @JsonProperty + private String type = "object"; + @JsonProperty + private String title; + @JsonProperty + private Map properties = new HashMap<>(); + @JsonProperty + private Set required = new HashSet<>(); + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public Set getRequired() { + return required; + } + + public void setRequired(Set required) { + this.required = required; + } +} diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/model/ValuesSchemaProperty.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/ValuesSchemaProperty.java new file mode 100644 index 0000000..0c3bd07 --- /dev/null +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/model/ValuesSchemaProperty.java @@ -0,0 +1,97 @@ +package io.quarkiverse.helm.deployment.model; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonInclude(NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ValuesSchemaProperty { + @JsonProperty + private String description; + @JsonProperty + private String type; + @JsonProperty + private Integer minimum; + @JsonProperty + private Integer maximum; + @JsonProperty + private String pattern; + @JsonProperty(value = "enum") + private Set enumValues; + @JsonProperty + private Map properties = new HashMap<>(); + @JsonProperty + private Set required = new HashSet<>(); + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Set getEnumValues() { + return enumValues; + } + + public void setEnumValues(Set enumValues) { + this.enumValues = enumValues; + } + + public Integer getMaximum() { + return maximum; + } + + public void setMaximum(Integer maximum) { + this.maximum = maximum; + } + + public Integer getMinimum() { + return minimum; + } + + public void setMinimum(Integer minimum) { + this.minimum = minimum; + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public Set getRequired() { + return required; + } + + public void setRequired(Set required) { + this.required = required; + } +} diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/FileUtils.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/FileUtils.java new file mode 100644 index 0000000..9b56153 --- /dev/null +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/FileUtils.java @@ -0,0 +1,28 @@ +package io.quarkiverse.helm.deployment.utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +public final class FileUtils { + private FileUtils() { + + } + + public static String[] lines(File file) throws IOException { + List lines = new ArrayList<>(); + try (FileInputStream is = new FileInputStream(file); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr)) { + String line; + while ((line = br.readLine()) != null) { + lines.add(line); + } + } + return lines.toArray(new String[0]); + } +} diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/HelmConfigUtils.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/HelmConfigUtils.java index b378ae0..6f426aa 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/HelmConfigUtils.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/HelmConfigUtils.java @@ -1,14 +1,13 @@ package io.quarkiverse.helm.deployment.utils; -import static io.github.yamlpath.utils.StringUtils.EMPTY; +import static org.apache.commons.lang3.StringUtils.EMPTY; import java.util.List; +import java.util.Objects; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.Stream; -import io.dekorate.helm.config.HelmChartConfig; -import io.dekorate.utils.Strings; +import io.quarkiverse.helm.deployment.HelmChartConfig; public final class HelmConfigUtils { @@ -19,13 +18,6 @@ private HelmConfigUtils() { } public static String deductProperty(HelmChartConfig helmConfig, String property) { - return deductProperty(helmConfig.getValuesRootAlias(), Stream.of(helmConfig.getDependencies()) - .map(d -> Strings.defaultIfEmpty(d.getAlias(), d.getName())) - .collect(Collectors.toList()), - property); - } - - public static String deductProperty(io.quarkiverse.helm.deployment.HelmChartConfig helmConfig, String property) { return deductProperty(helmConfig.valuesRootAlias(), helmConfig.dependencies().entrySet().stream() .map(entry -> entry.getValue().alias().orElseGet(() -> entry.getValue().name().orElse(entry.getKey()))) .collect(Collectors.toList()), @@ -48,7 +40,7 @@ private static String deductProperty(String valuesRootAlias, List depend } private static boolean startWithDependencyPrefix(String property, List dependencies) { - if (dependencies == null || dependencies.size() == 0) { + if (dependencies == null || dependencies.isEmpty()) { return false; } @@ -58,6 +50,6 @@ private static boolean startWithDependencyPrefix(String property, List d } String name = parts[0]; - return dependencies.stream().anyMatch(d -> Strings.equals(d, name)); + return dependencies.stream().anyMatch(d -> Objects.equals(d, name)); } } diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/HelmTarArchiver.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/HelmTarArchiver.java new file mode 100644 index 0000000..cee63d0 --- /dev/null +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/HelmTarArchiver.java @@ -0,0 +1,76 @@ +package io.quarkiverse.helm.deployment.utils; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; +import org.apache.commons.compress.utils.IOUtils; + +public final class HelmTarArchiver { + + private HelmTarArchiver() { + + } + + public static File createTarBall(File outputFile, File inputDirectory, List fileList, String compression, + Consumer tarArchiveEntryCustomizer) throws IOException { + + try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile); + BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream)) { + + final TarArchiveOutputStream tarArchiveOutputStream; + if (isGzip(compression)) { + tarArchiveOutputStream = new TarArchiveOutputStream(new GzipCompressorOutputStream(bufferedOutputStream)); + } else if (isBzip2(compression)) { + tarArchiveOutputStream = new TarArchiveOutputStream(new BZip2CompressorOutputStream(bufferedOutputStream)); + } else { + tarArchiveOutputStream = new TarArchiveOutputStream(bufferedOutputStream); + } + tarArchiveOutputStream.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); + tarArchiveOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + for (File currentFile : fileList) { + + String relativeFilePath = inputDirectory.toURI().relativize( + new File(currentFile.getAbsolutePath()).toURI()).getPath(); + + final TarArchiveEntry tarEntry = new TarArchiveEntry(currentFile, relativeFilePath); + tarEntry.setSize(currentFile.length()); + if (currentFile.isDirectory()) { + tarEntry.setSize(0L); + tarEntry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE); + } + Optional.ofNullable(tarArchiveEntryCustomizer).ifPresent(tac -> tac.accept(tarEntry)); + tarArchiveOutputStream.putArchiveEntry(tarEntry); + if (currentFile.isFile()) { + try (InputStream fis = new FileInputStream(currentFile)) { + IOUtils.copy(fis, tarArchiveOutputStream); + } + } + tarArchiveOutputStream.closeArchiveEntry(); + } + tarArchiveOutputStream.close(); + } + + return outputFile; + } + + private static boolean isGzip(String compression) { + return compression.equalsIgnoreCase("tar.gz") || compression.equalsIgnoreCase("tgz"); + } + + private static boolean isBzip2(String compression) { + return compression.equalsIgnoreCase("tar.bz") + || compression.equalsIgnoreCase("tar.bzip2") + || compression.equalsIgnoreCase("tar.bz2"); + } +} diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ReadmeBuilder.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ReadmeBuilder.java index 402a751..4464891 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ReadmeBuilder.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ReadmeBuilder.java @@ -4,8 +4,9 @@ import java.util.SortedSet; import java.util.TreeSet; -import io.dekorate.helm.config.HelmChartConfig; -import io.dekorate.utils.Strings; +import org.apache.commons.lang3.StringUtils; + +import io.quarkiverse.helm.deployment.HelmChartConfig; public final class ReadmeBuilder { @@ -46,7 +47,7 @@ private void writeTableRow(Object... values) { for (Object value : values) { String actualValue = SPACE; if (value != null) { - actualValue = Strings.defaultIfEmpty(value.toString(), SPACE); + actualValue = StringUtils.defaultIfEmpty(value.toString(), SPACE); } row.append(TABLE_SEPARATOR).append(SPACE).append(actualValue).append(SPACE); @@ -56,7 +57,7 @@ private void writeTableRow(Object... values) { } private void writeLine(String line, Object... args) { - if (Strings.isNotNullOrEmpty(line)) { + if (StringUtils.isNotEmpty(line)) { sb.append(String.format(line, args)); } @@ -80,14 +81,12 @@ private void writeTip(String message) { writeLine(TIP + message); } - public static String build(HelmChartConfig helmConfig, Map values) { + public static String build(String name, HelmChartConfig helmConfig, Map values) { ReadmeBuilder builder = new ReadmeBuilder(); // Title: // # {chart.name} - builder.writeHeader(H1, helmConfig.getName()); - if (Strings.isNotNullOrEmpty(helmConfig.getDescription())) { - builder.writeLine(helmConfig.getDescription()); - } + builder.writeHeader(H1, name); + helmConfig.description().ifPresent(builder::writeLine); // Configuration: builder.writeHeader(H2, "Configuration"); diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/SystemPropertiesUtils.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/SystemPropertiesUtils.java index 2d43321..713ef33 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/SystemPropertiesUtils.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/SystemPropertiesUtils.java @@ -2,14 +2,14 @@ import static io.dekorate.utils.Strings.defaultIfEmpty; import static io.github.yamlpath.utils.StringUtils.isNullOrEmpty; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; -import io.dekorate.utils.Strings; - public final class SystemPropertiesUtils { private static final String SYSTEM_PROPERTY_START = "${"; @@ -20,7 +20,7 @@ private SystemPropertiesUtils() { } public static boolean hasSystemProperties(String rawValue) { - return Strings.isNotNullOrEmpty(rawValue) && rawValue.contains(SYSTEM_PROPERTY_START); + return isNotEmpty(rawValue) && rawValue.contains(SYSTEM_PROPERTY_START); } public static List getSystemProperties(String str) { @@ -35,13 +35,13 @@ public static String getPropertyFromSystem(String propertyName, String defaultVa } private static List substringsBetween(String str, String open, String close) { - if (Strings.isNullOrEmpty(str) || Strings.isNullOrEmpty(open) || Strings.isNullOrEmpty(close)) { + if (isEmpty(str) || isEmpty(open) || isEmpty(close)) { return Collections.emptyList(); } int closeLen = close.length(); int openLen = open.length(); - List list = new ArrayList(); + List list = new ArrayList<>(); int end; for (int pos = 0; pos < str.length() - closeLen; pos = end + closeLen) { int start = str.indexOf(open, pos); diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ValuesHolder.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ValuesHolder.java index ea9a1ad..6519b9b 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ValuesHolder.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ValuesHolder.java @@ -4,8 +4,9 @@ import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; + import io.dekorate.ConfigReference; -import io.dekorate.utils.Strings; public class ValuesHolder { private final Map prodValues = new HashMap<>(); @@ -37,12 +38,8 @@ public void putIfAbsent(String property, ConfigReference config, Object value, S public Map get(String profile) { Map values = prodValues; - if (Strings.isNotNullOrEmpty(profile)) { - values = valuesByProfile.get(profile); - if (values == null) { - values = new HashMap<>(); - valuesByProfile.put(profile, values); - } + if (StringUtils.isNotEmpty(profile)) { + values = valuesByProfile.computeIfAbsent(profile, p -> new HashMap<>()); } return values; diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ValuesSchemaUtils.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ValuesSchemaUtils.java index 9c1195b..e052c08 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ValuesSchemaUtils.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/ValuesSchemaUtils.java @@ -1,19 +1,21 @@ package io.quarkiverse.helm.deployment.utils; -import static io.dekorate.helm.util.HelmConfigUtils.deductProperty; +import static io.quarkiverse.helm.deployment.utils.HelmConfigUtils.deductProperty; import java.util.Collection; import java.util.Map; import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; + import com.fasterxml.jackson.core.type.TypeReference; import io.dekorate.ConfigReference; -import io.dekorate.helm.config.HelmChartConfig; -import io.dekorate.helm.model.ValuesSchema; -import io.dekorate.helm.model.ValuesSchemaProperty; import io.dekorate.utils.Serialization; -import io.dekorate.utils.Strings; +import io.quarkiverse.helm.deployment.HelmChartConfig; +import io.quarkiverse.helm.deployment.ValuesSchemaPropertyConfig; +import io.quarkiverse.helm.deployment.model.ValuesSchema; +import io.quarkiverse.helm.deployment.model.ValuesSchemaProperty; public final class ValuesSchemaUtils { private ValuesSchemaUtils() { @@ -23,7 +25,7 @@ private ValuesSchemaUtils() { public static Map createSchema(HelmChartConfig helmConfig, Map prodValues) { ValuesSchema schema = new ValuesSchema(); - schema.setTitle(helmConfig.getValuesSchema().getTitle()); + schema.setTitle(helmConfig.valuesSchema().title()); // from value references for (Map.Entry value : prodValues.entrySet()) { @@ -78,8 +80,11 @@ public static Map createSchema(HelmChartConfig helmConfig, } // from properties - for (io.dekorate.helm.config.ValuesSchemaProperty propertyFromConfig : helmConfig.getValuesSchema().getProperties()) { - String[] tree = deductProperty(helmConfig, propertyFromConfig.getName()).split(Pattern.quote(".")); + for (Map.Entry propertyFromConfig : helmConfig.valuesSchema().properties() + .entrySet()) { + String name = propertyFromConfig.getValue().name().orElse(propertyFromConfig.getKey()); + + String[] tree = deductProperty(helmConfig, name).split(Pattern.quote(".")); ValuesSchemaProperty parent = null; Map location = schema.getProperties(); for (int index = 0; index < tree.length - 1; index++) { @@ -97,23 +102,12 @@ public static Map createSchema(HelmChartConfig helmConfig, String propertyName = tree[tree.length - 1]; ValuesSchemaProperty property = location.getOrDefault(propertyName, new ValuesSchemaProperty()); - if (Strings.isNotNullOrEmpty(propertyFromConfig.getDescription())) { - property.setDescription(propertyFromConfig.getDescription()); - } - - if (Strings.isNotNullOrEmpty(propertyFromConfig.getPattern())) { - property.setPattern(propertyFromConfig.getPattern()); - } - - if (propertyFromConfig.getMaximum() != Integer.MAX_VALUE) { - property.setMaximum(propertyFromConfig.getMaximum()); - } - - if (propertyFromConfig.getMinimum() != Integer.MIN_VALUE) { - property.setMinimum(propertyFromConfig.getMinimum()); - } + propertyFromConfig.getValue().description().ifPresent(property::setDescription); + propertyFromConfig.getValue().pattern().ifPresent(property::setPattern); + propertyFromConfig.getValue().maximum().ifPresent(property::setMaximum); + propertyFromConfig.getValue().minimum().ifPresent(property::setMinimum); - if (propertyFromConfig.isRequired()) { + if (propertyFromConfig.getValue().required()) { if (parent == null) { schema.getRequired().add(propertyName); } else { @@ -121,8 +115,8 @@ public static Map createSchema(HelmChartConfig helmConfig, } } - if (Strings.isNotNullOrEmpty(propertyFromConfig.getType())) { - property.setType(propertyFromConfig.getType()); + if (StringUtils.isNotEmpty(propertyFromConfig.getValue().type())) { + property.setType(propertyFromConfig.getValue().type()); } location.put(propertyName, property); diff --git a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/YamlExpressionParserUtils.java b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/YamlExpressionParserUtils.java index 15ea946..b235545 100644 --- a/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/YamlExpressionParserUtils.java +++ b/deployment/src/main/java/io/quarkiverse/helm/deployment/utils/YamlExpressionParserUtils.java @@ -4,8 +4,9 @@ import java.util.Set; import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; + import io.dekorate.ConfigReference; -import io.dekorate.utils.Strings; import io.github.yamlpath.YamlExpressionParser; public final class YamlExpressionParserUtils { @@ -40,7 +41,7 @@ public static Object readAndSet(YamlExpressionParser parser, String path, String public static String toExpression(String property, Object provided, Object found, ConfigReference valueReference) { Optional expressionProvided = Optional.ofNullable(valueReference.getExpression()) - .filter(Strings::isNotNullOrEmpty); + .filter(StringUtils::isNotEmpty); if (expressionProvided.isPresent()) { return expressionProvided.get(); diff --git a/docs/modules/ROOT/pages/includes/attributes.adoc b/docs/modules/ROOT/pages/includes/attributes.adoc index 032566b..749e1ab 100644 --- a/docs/modules/ROOT/pages/includes/attributes.adoc +++ b/docs/modules/ROOT/pages/includes/attributes.adoc @@ -1,4 +1,4 @@ -:quarkus-version: 3.5.0 +:quarkus-version: 3.5.1 :quarkus-helm-version: 1.2.0 :maven-version: 3.8.1+ diff --git a/pom.xml b/pom.xml index 4fbf024..4dc48d3 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ UTF-8 UTF-8 3.5.1 + 0.0.10 @@ -42,6 +43,11 @@ pom import + + io.github.yaml-path + yaml-path + ${yaml-path.version} +