From a80ecbdb2bba680b173b32e6104133b112e6041c Mon Sep 17 00:00:00 2001 From: manusa Date: Fri, 25 Sep 2020 17:35:15 +0200 Subject: [PATCH] fix: k8s:resource replaces template variables in provided fragments, k8s:helm doesn't. - Moved logic from ResourceMojo to separate service - Fixed Resource generation, template placeholders are replaced only in aggregated YAML file (kubernetes.yml) not in individual resource files. - Placeholder/variable replacement can be turned off (jkube.interpolateTemplateParameters) - Fixed Helm chart generation, template placeholders are NOT replaced. Provided support for Parameters with no default value (values.yml) will only contain those which have a default. The rest of parameters can be specified with `--set` option - Added integration test to verify behavior for resource and helm YAML generation. Signed-off-by: Marc Nuri WIP fix: k8s:resource replaces template variables in provided fragments, k8s:helm doesn't. - Moved logic from ResourceMojo to separate service - Fixed Resource generation, template placeholders are replaced only in aggregated YAML file (kubernetes.yml) not in individual resource files. - Placeholder/variable replacement can be turned off (jkube.interpolateTemplateParameters) - Fixed Helm chart generation, template placeholders are NOT replaced. Provided support for Parameters with no default value (values.yml) will only contain those which have a default. The rest of parameters can be specified with `--set` option - Added integration test to verify behavior for resource and helm YAML generation. Signed-off-by: Marc Nuri --- CHANGELOG.md | 3 +- .../kit/build/service/docker/helper/Task.java | 1 + .../kit/config/service/EnricherManager.java | 28 ++ .../kit/config/service/JKubeServiceHub.java | 10 +- .../kit/config/service/ResourceService.java | 37 ++ .../config/service/ResourceServiceConfig.java | 46 +++ jkube-kit/parent/pom.xml | 14 +- jkube-kit/pom.xml | 1 + .../jkube/kit/resource/helm/HelmService.java | 5 +- jkube-kit/resource/service/pom.xml | 55 +++ .../service/DefaultResourceService.java | 153 +++++++++ .../kit/resource/service/TemplateUtil.java | 90 +++++ .../jkube/kit/resource/service/WriteUtil.java | 83 +++++ .../service/DefaultResourceServiceTest.java | 93 ++++++ .../resource/service/TemplateUtilTest.java | 167 ++++++++++ .../kit/resource/service/WriteUtilTest.java | 126 +++++++ .../inc/goals/build/_jkube-resource.adoc | 14 + .../expected/helm/Chart.yaml | 17 + .../helm-and-fragments-deployment.yaml | 70 ++++ .../expected/helm/values.yaml | 17 + .../expected/resource/kubernetes.yml | 97 ++++++ .../helm-and-fragments-deployment.yml | 70 ++++ .../it/helm-and-fragments/invoker.properties | 16 + .../it/src/it/helm-and-fragments/pom.xml | 37 ++ .../src/main/jkube/deployment.yml | 37 ++ .../src/main/jkube/first-template.yml | 18 + .../src/main/jkube/template.yml | 19 ++ .../src/it/helm-and-fragments/verify.groovy | 30 ++ .../org/eclipse/jkube/maven/it/Verify.java | 6 +- kubernetes-maven-plugin/plugin/pom.xml | 8 +- ...nager.java => DefaultEnricherManager.java} | 20 +- .../plugin/mojo/build/AbstractDockerMojo.java | 4 +- .../plugin/mojo/build/AbstractJKubeMojo.java | 21 +- .../maven/plugin/mojo/build/ResourceMojo.java | 315 ++++-------------- 34 files changed, 1452 insertions(+), 276 deletions(-) create mode 100644 jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/EnricherManager.java create mode 100644 jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ResourceService.java create mode 100644 jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ResourceServiceConfig.java create mode 100644 jkube-kit/resource/service/pom.xml create mode 100644 jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/DefaultResourceService.java create mode 100644 jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/TemplateUtil.java create mode 100644 jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/WriteUtil.java create mode 100644 jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/DefaultResourceServiceTest.java create mode 100644 jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/TemplateUtilTest.java create mode 100644 jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/WriteUtilTest.java create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/Chart.yaml create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/templates/helm-and-fragments-deployment.yaml create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/values.yaml create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/resource/kubernetes.yml create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/resource/kubernetes/helm-and-fragments-deployment.yml create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/invoker.properties create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/pom.xml create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/deployment.yml create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/first-template.yml create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/template.yml create mode 100644 kubernetes-maven-plugin/it/src/it/helm-and-fragments/verify.groovy rename kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/enricher/{EnricherManager.java => DefaultEnricherManager.java} (85%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e00666742..d19883647d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ Usage: * Fix #384: Enricher defined Container environment variables get merged with vars defined in Image Build Configuration * Fix #385: WildFly Bootable JAR - Add native support for slim Bootable JAR * Fix #415: Fixed resource validation for new json-schema-validator version +* Fix #356: Add further support for tls termination and insecureEdgeTerminationPolicy in pom.xml +* Fix #327: k8s:resource replaces template variables in provided fragments, k8s:helm doesn't. ### 1.0.0 (2020-09-09) * Fix #351: Fix AutoTLSEnricher - add annotation + volume config to resource @@ -47,7 +49,6 @@ Usage: * Fix #362: Quarkus supports S2I builds both for JVM and native mode * Fix #297: Added missing documentation for WatchMojo configuration parameters * Fix #371: WebAppGenerator path configuration is no longer ignored -* Fix #356: Add further support for tls termination and insecureEdgeTerminationPolicy in pom.xml ### 1.0.0-rc-1 (2020-07-23) * Fix #252: Replace Quarkus Native Base image with ubi-minimal (same as in `Dockerfile.native`) diff --git a/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/helper/Task.java b/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/helper/Task.java index 2dc33ebf42..f54e6bdd41 100644 --- a/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/helper/Task.java +++ b/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/helper/Task.java @@ -16,6 +16,7 @@ /** * Represents a generic task to be executed on a object. */ +@FunctionalInterface public interface Task { void execute(T object) throws Exception; diff --git a/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/EnricherManager.java b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/EnricherManager.java new file mode 100644 index 0000000000..6423f18d1f --- /dev/null +++ b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/EnricherManager.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.config.service; + +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import org.eclipse.jkube.kit.config.resource.PlatformMode; +import org.eclipse.jkube.kit.config.resource.ProcessorConfig; + +public interface EnricherManager { + + void createDefaultResources(PlatformMode platformMode, final KubernetesListBuilder builder); + void createDefaultResources(PlatformMode platformMode, ProcessorConfig enricherConfig, final KubernetesListBuilder builder); + + void enrich(PlatformMode platformMode, final KubernetesListBuilder builder); + void enrich(PlatformMode platformMode, final ProcessorConfig enricherConfig, final KubernetesListBuilder builder); + +} diff --git a/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/JKubeServiceHub.java b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/JKubeServiceHub.java index c298c44d61..a5b226f62e 100644 --- a/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/JKubeServiceHub.java +++ b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/JKubeServiceHub.java @@ -62,6 +62,7 @@ public class JKubeServiceHub implements Closeable { private KubernetesClient client; private LazyBuilder artifactResolverService; private LazyBuilder buildService; + private LazyBuilder resourceService; private LazyBuilder applyService; private LazyBuilder undeployService; private LazyBuilder migrateService; @@ -69,13 +70,16 @@ public class JKubeServiceHub implements Closeable { @Builder public JKubeServiceHub( ClusterAccess clusterAccess, RuntimeMode platformMode, KitLogger log, - ServiceHub dockerServiceHub, JKubeConfiguration configuration, BuildServiceConfig buildServiceConfig) { + ServiceHub dockerServiceHub, JKubeConfiguration configuration, + BuildServiceConfig buildServiceConfig, + LazyBuilder resourceService) { this.clusterAccess = clusterAccess; this.platformMode = platformMode; this.log = log; this.dockerServiceHub = dockerServiceHub; this.configuration = configuration; this.buildServiceConfig = buildServiceConfig; + this.resourceService = resourceService; init(); } @@ -136,6 +140,10 @@ public BuildService getBuildService() { return buildService.get(); } + public ResourceService getResourceService() { + return resourceService.get(); + } + public ApplyService getApplyService() { return applyService.get(); } diff --git a/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ResourceService.java b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ResourceService.java new file mode 100644 index 0000000000..acc51323d7 --- /dev/null +++ b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ResourceService.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.config.service; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jkube.kit.common.KitLogger; +import org.eclipse.jkube.kit.common.util.ResourceClassifier; +import org.eclipse.jkube.kit.config.resource.PlatformMode; + +import io.fabric8.kubernetes.api.model.KubernetesList; + +public interface ResourceService { + + KubernetesList generateResources(PlatformMode platformMode, EnricherManager enricherManager, KitLogger log) + throws IOException; + + File writeResources(KubernetesList resources, ResourceClassifier classifier, KitLogger log) throws IOException; + + @FunctionalInterface + interface ResourceFileProcessor { + + File[] processResources(File[] resources) throws IOException; + } +} diff --git a/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ResourceServiceConfig.java b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ResourceServiceConfig.java new file mode 100644 index 0000000000..78b14eced2 --- /dev/null +++ b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/ResourceServiceConfig.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.config.service; + +import java.io.File; + +import org.eclipse.jkube.kit.common.JavaProject; +import org.eclipse.jkube.kit.common.ResourceFileType; +import org.eclipse.jkube.kit.config.resource.ResourceConfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Class to hold configuration parameters for the Resource service. + */ +@Builder(toBuilder = true) +@AllArgsConstructor +@NoArgsConstructor +@Getter +@EqualsAndHashCode +public class ResourceServiceConfig { + + private JavaProject project; + private File resourceDir; + private File targetDir; + private ResourceFileType resourceFileType; + private ResourceConfig resourceConfig; + private ResourceService.ResourceFileProcessor resourceFilesProcessor; + private boolean interpolateTemplateParameters; + +} diff --git a/jkube-kit/parent/pom.xml b/jkube-kit/parent/pom.xml index 163def828f..6484a06b3a 100644 --- a/jkube-kit/parent/pom.xml +++ b/jkube-kit/parent/pom.xml @@ -219,13 +219,25 @@ ${project.version} + + org.eclipse.jkube + jkube-kit-resource-service + ${project.version} + + org.eclipse.jkube jkube-kit-resource-helm ${project.version} - + + org.eclipse.jkube + jkube-kit-profiles + ${project.version} + + + io.fabric8 kubernetes-client diff --git a/jkube-kit/pom.xml b/jkube-kit/pom.xml index 588d04b089..5d7cfa4666 100644 --- a/jkube-kit/pom.xml +++ b/jkube-kit/pom.xml @@ -51,6 +51,7 @@ enricher/generic enricher/specific profile + resource/service resource/helm watcher/api watcher/standard diff --git a/jkube-kit/resource/helm/src/main/java/org/eclipse/jkube/kit/resource/helm/HelmService.java b/jkube-kit/resource/helm/src/main/java/org/eclipse/jkube/kit/resource/helm/HelmService.java index 4cffe69ce2..14395b4843 100644 --- a/jkube-kit/resource/helm/src/main/java/org/eclipse/jkube/kit/resource/helm/HelmService.java +++ b/jkube-kit/resource/helm/src/main/java/org/eclipse/jkube/kit/resource/helm/HelmService.java @@ -201,8 +201,9 @@ private static void createTemplateParameters(HelmConfig helmConfig, File outputD .orElse(Collections.emptyList()).stream() .map(Template::getParameters).flatMap(List::stream) .map(HelmParameter::new).collect(Collectors.toList()); - final Map values = helmParameters.stream().collect(Collectors.toMap( - HelmParameter::getHelmName, hp -> hp.getParameter().getValue())); + final Map values = helmParameters.stream() + .filter(hp -> hp.getParameter().getValue() != null) + .collect(Collectors.toMap(HelmParameter::getHelmName, hp -> hp.getParameter().getValue())); File outputChartFile = new File(outputDir, VALUES_FILENAME); ResourceUtil.save(outputChartFile, values, ResourceFileType.yaml); diff --git a/jkube-kit/resource/service/pom.xml b/jkube-kit/resource/service/pom.xml new file mode 100644 index 0000000000..08a12c282f --- /dev/null +++ b/jkube-kit/resource/service/pom.xml @@ -0,0 +1,55 @@ + + + + 4.0.0 + + + org.eclipse.jkube + jkube-kit-parent + 1.0.1-SNAPSHOT + ../../parent/pom.xml + + + jkube-kit-resource-service + + JKube Kit :: Resource :: Service + + + + + org.eclipse.jkube + jkube-kit-enricher-api + + + org.eclipse.jkube + jkube-kit-profiles + + + + junit + junit + + + org.jmockit + jmockit + + + org.assertj + assertj-core + + + diff --git a/jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/DefaultResourceService.java b/jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/DefaultResourceService.java new file mode 100644 index 0000000000..6bb1efbc7a --- /dev/null +++ b/jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/DefaultResourceService.java @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.resource.service; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.validation.ConstraintViolationException; + +import org.eclipse.jkube.kit.common.KitLogger; +import org.eclipse.jkube.kit.common.ResourceFileType; +import org.eclipse.jkube.kit.common.util.JKubeProjectUtil; +import org.eclipse.jkube.kit.common.util.ResourceClassifier; +import org.eclipse.jkube.kit.common.util.ValidationUtil; +import org.eclipse.jkube.kit.config.resource.PlatformMode; +import org.eclipse.jkube.kit.config.resource.ProcessorConfig; +import org.eclipse.jkube.kit.config.resource.ResourceConfig; +import org.eclipse.jkube.kit.config.service.EnricherManager; +import org.eclipse.jkube.kit.config.service.ResourceService; +import org.eclipse.jkube.kit.config.service.ResourceServiceConfig; +import org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil; +import org.eclipse.jkube.kit.profile.Profile; +import org.eclipse.jkube.kit.profile.ProfileUtil; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesList; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; + +import static org.eclipse.jkube.kit.common.util.KubernetesHelper.listResourceFragments; +import static org.eclipse.jkube.kit.resource.service.TemplateUtil.interpolateTemplateVariables; +import static org.eclipse.jkube.kit.resource.service.WriteUtil.writeResourcesIndividualAndComposite; + +public class DefaultResourceService implements ResourceService { + + private final ResourceServiceConfig resourceServiceConfig; + + public DefaultResourceService(ResourceServiceConfig resourceServiceConfig) { + this.resourceServiceConfig = resourceServiceConfig; + } + + @Override + public KubernetesList generateResources(PlatformMode platformMode, EnricherManager enricherManager, KitLogger log) + throws IOException { + + // Generate all resources from the main resource directory, configuration and create them accordingly + return generateAppResources(platformMode, enricherManager, log) + .addAllToItems(generateProfiledResourcesFromSubdirectories(platformMode, enricherManager)) + .build(); + } + + @Override + public File writeResources(KubernetesList resources, ResourceClassifier classifier, KitLogger log) throws IOException { + final File targetDir = resourceServiceConfig.getTargetDir(); + final ResourceFileType resourceFileType = resourceServiceConfig.getResourceFileType(); + // write kubernetes.yml / openshift.yml + File resourceFileBase = new File(targetDir, classifier.getValue()); + + File file = writeResourcesIndividualAndComposite(resources, resourceFileBase, resourceFileType, log); + // Resolve template placeholders + if (resourceServiceConfig.isInterpolateTemplateParameters()) { + interpolateTemplateVariables(resources, file); + } + + return file; + } + + private KubernetesListBuilder generateAppResources(PlatformMode platformMode, EnricherManager enricherManager, KitLogger log) + throws IOException { + + final ResourceConfig resourceConfig = resourceServiceConfig.getResourceConfig(); + final File resourceDir = resourceServiceConfig.getResourceDir(); + try { + File[] resourceFiles = listResourceFragments(resourceDir, resourceConfig !=null ? resourceConfig.getRemotes() : null, log); + log.info("Using resource templates from %s", resourceDir); + final File[] processedResource = processResourceFiles(resourceFiles); + KubernetesListBuilder builder = processResourceFragments(platformMode, processedResource); + + // Create default resources for app resources only + enricherManager.createDefaultResources(platformMode, builder); + + // Enrich descriptors + enricherManager.enrich(platformMode, builder); + + return builder; + } catch (ConstraintViolationException e) { + String message = ValidationUtil.createValidationMessage(e.getConstraintViolations()); + log.error("ConstraintViolationException: %s", message); + throw new IOException(message, e); + } + } + + private KubernetesListBuilder processResourceFragments(PlatformMode platformMode, File[] resourceFiles) throws IOException { + final KubernetesListBuilder builder = new KubernetesListBuilder(); + // Add resource files found in the JKube directory + if (resourceFiles != null && resourceFiles.length > 0) { + builder.addAllToItems(readResourceFragments(platformMode, resourceFiles).buildItems()); + } + return builder; + } + + private List generateProfiledResourcesFromSubdirectories( + PlatformMode platformMode, EnricherManager enricherManager) throws IOException { + + final List ret = new ArrayList<>(); + final File resourceDir = resourceServiceConfig.getResourceDir(); + File[] profileDirs = resourceDir.listFiles(File::isDirectory); + if (profileDirs != null) { + for (File profileDir : profileDirs) { + Profile foundProfile = ProfileUtil.findProfile(profileDir.getName(), resourceDir); + ProcessorConfig enricherConfig = foundProfile.getEnricherConfig(); + File[] resourceFiles = listResourceFragments(profileDir); + final File[] processedResources = processResourceFiles(resourceFiles); + if (processedResources.length > 0) { + KubernetesListBuilder profileBuilder = readResourceFragments(platformMode, processedResources); + enricherManager.createDefaultResources(platformMode, enricherConfig, profileBuilder); + enricherManager.enrich(platformMode, enricherConfig, profileBuilder); + ret.addAll(profileBuilder.buildItems()); + } + } + } + return ret; + } + + private KubernetesListBuilder readResourceFragments(PlatformMode platformMode, File[] resourceFiles) throws IOException { + return KubernetesResourceUtil.readResourceFragmentsFrom( + platformMode, + KubernetesResourceUtil.DEFAULT_RESOURCE_VERSIONING, + JKubeProjectUtil.createDefaultResourceName(resourceServiceConfig.getProject().getArtifactId()), + resourceFiles); + } + + private File[] processResourceFiles(File[] resourceFiles) throws IOException { + if (resourceServiceConfig.getResourceFilesProcessor() != null) { + return resourceServiceConfig.getResourceFilesProcessor().processResources(resourceFiles); + } + return resourceFiles; + } + + +} diff --git a/jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/TemplateUtil.java b/jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/TemplateUtil.java new file mode 100644 index 0000000000..bc1808e720 --- /dev/null +++ b/jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/TemplateUtil.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.resource.service; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.stream.Collectors; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesList; +import io.fabric8.openshift.api.model.Parameter; +import io.fabric8.openshift.api.model.Template; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; + +class TemplateUtil { + + private TemplateUtil() { } + + /** + * Returns the Template if the list contains a single Template only otherwise returns null + */ + static Template getSingletonTemplate(KubernetesList resources) { + // if the list contains a single Template lets unwrap it + if (resources != null) { + List items = resources.getItems(); + if (items != null && items.size() == 1) { + HasMetadata singleEntity = items.get(0); + if (singleEntity instanceof Template) { + return (Template) singleEntity; + } + } + } + return null; + } + + static void interpolateTemplateVariables(KubernetesList resources, File kubernetesYaml) throws IOException { + final List parameters = listAllParameters(resources); + if (parameters.isEmpty()) { + return; + } + String kubernetesYamlContent; + try { + kubernetesYamlContent = FileUtils.readFileToString(kubernetesYaml, Charset.defaultCharset()); + } catch (IOException e) { + throw new IOException("Failed to load " + kubernetesYaml + " for template variable replacement", e); + } + final String interpolatedKubernetesYamlContent = interpolateTemplateVariables(parameters, kubernetesYamlContent); + if (!kubernetesYamlContent.equals(interpolatedKubernetesYamlContent)) { + try { + FileUtils.writeStringToFile(kubernetesYaml, interpolatedKubernetesYamlContent, Charset.defaultCharset()); + } catch (IOException e) { + throw new IOException("Failed to save " + kubernetesYaml + " after replacing template expressions", e); + } + } + } + + private static String interpolateTemplateVariables(List parameters, String text) { + for (Parameter parameter : parameters) { + final String from = "${" + parameter.getName() + "}"; + final String to = parameter.getValue(); + if (StringUtils.isNotBlank(to)) { + text = text.replace(from, to); + } + } + return text; + } + + private static List listAllParameters(KubernetesList resources) { + return resources.getItems().stream() + .filter(template -> template instanceof Template) + .map(Template.class::cast) + .map(Template::getParameters) + .flatMap(List::stream) + .collect(Collectors.toList()); + } +} diff --git a/jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/WriteUtil.java b/jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/WriteUtil.java new file mode 100644 index 0000000000..b4d3951258 --- /dev/null +++ b/jkube-kit/resource/service/src/main/java/org/eclipse/jkube/kit/resource/service/WriteUtil.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.resource.service; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesList; +import io.fabric8.openshift.api.model.Template; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jkube.kit.common.KitLogger; +import org.eclipse.jkube.kit.common.ResourceFileType; +import org.eclipse.jkube.kit.common.util.KubernetesHelper; +import org.eclipse.jkube.kit.common.util.ResourceUtil; +import org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil; + +import java.io.File; +import java.io.IOException; + +import static org.eclipse.jkube.kit.resource.service.TemplateUtil.getSingletonTemplate; + +class WriteUtil { + + private WriteUtil(){ } + + static File writeResourcesIndividualAndComposite( + KubernetesList resources, File resourceFileBase, ResourceFileType resourceFileType, KitLogger log) throws IOException { + + // entity is object which will be sent to writeResource for openshift.yml + // if generateRoute is false, this will be set to resources with new list + // otherwise it will be set to resources with old list. + Object entity = resources; + + // if the list contains a single Template lets unwrap it + // in resources already new or old as per condition is set. + // no need to worry about this for dropping Route. + Template template = getSingletonTemplate(resources); + if (template != null) { + entity = template; + } + + File file = writeResource(resourceFileBase, entity, resourceFileType); + + // write separate files, one for each resource item + // resources passed to writeIndividualResources is also new one. + writeIndividualResources(resources, resourceFileBase, resourceFileType, log); + return file; + } + + private static void writeIndividualResources(KubernetesList resources, File targetDir, + ResourceFileType resourceFileType, KitLogger log) throws IOException { + for (HasMetadata item : resources.getItems()) { + String name = KubernetesHelper.getName(item); + if (StringUtils.isBlank(name)) { + log.error("No name for generated item %s", item); + continue; + } + String itemFile = KubernetesResourceUtil.getNameWithSuffix(name, item.getKind()); + + // Here we are writing individual file for all the resources. + File itemTarget = new File(targetDir, itemFile); + writeResource(itemTarget, item, resourceFileType); + } + } + + static File writeResource(File resourceFileBase, Object entity, ResourceFileType resourceFileType) + throws IOException { + try { + return ResourceUtil.save(resourceFileBase, entity, resourceFileType); + } catch (IOException e) { + throw new IOException("Failed to write resource to " + resourceFileBase + ".", e); + } + } +} diff --git a/jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/DefaultResourceServiceTest.java b/jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/DefaultResourceServiceTest.java new file mode 100644 index 0000000000..bb50cc322d --- /dev/null +++ b/jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/DefaultResourceServiceTest.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.resource.service; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jkube.kit.common.KitLogger; +import org.eclipse.jkube.kit.common.ResourceFileType; +import org.eclipse.jkube.kit.common.util.ResourceClassifier; +import org.eclipse.jkube.kit.config.resource.PlatformMode; +import org.eclipse.jkube.kit.config.resource.ResourceConfig; +import org.eclipse.jkube.kit.config.service.EnricherManager; +import org.eclipse.jkube.kit.config.service.ResourceServiceConfig; + +import io.fabric8.kubernetes.api.model.KubernetesList; +import mockit.Mocked; +import mockit.Verifications; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DefaultResourceServiceTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @SuppressWarnings("unused") + @Mocked + private EnricherManager enricherManager; + @SuppressWarnings("unused") + @Mocked + private KitLogger kitLogger; + @SuppressWarnings("unused") + @Mocked + private ResourceConfig resourceConfig; + + private File targetDir; + private ResourceServiceConfig resourceServiceConfig; + private DefaultResourceService defaultResourceService; + + @Before + public void init() throws IOException { + targetDir = temporaryFolder.newFolder("target"); + resourceServiceConfig = ResourceServiceConfig.builder() + .interpolateTemplateParameters(true) + .targetDir(targetDir) + .resourceFileType(ResourceFileType.yaml) + .resourceDir(temporaryFolder.newFolder("resources")) + .resourceConfig(resourceConfig) + .build(); + defaultResourceService = new DefaultResourceService(resourceServiceConfig); + } + + @Test + public void generateResourcesWithNoResourcesShouldReturnEmpty() throws IOException { + // When + final KubernetesList result = defaultResourceService + .generateResources(PlatformMode.kubernetes, enricherManager, kitLogger); + // Then + assertThat(result.getItems()).isEmpty(); + } + + @SuppressWarnings("AccessStaticViaInstance") + @Test + public void writeResources(@Mocked WriteUtil writeUtil, @Mocked TemplateUtil templateUtil) throws IOException { + // When + defaultResourceService.writeResources(null, ResourceClassifier.KUBERNETES, kitLogger); + // Then + // @formatter:off + new Verifications() {{ + writeUtil.writeResourcesIndividualAndComposite( + null, new File(targetDir, "kubernetes"), ResourceFileType.yaml, kitLogger); + times = 1; + templateUtil.interpolateTemplateVariables(null, (File)any); + times = 1; + }}; + // @formatter:on + } +} diff --git a/jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/TemplateUtilTest.java b/jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/TemplateUtilTest.java new file mode 100644 index 0000000000..33fc432024 --- /dev/null +++ b/jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/TemplateUtilTest.java @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.resource.service; + +import java.io.IOException; +import java.nio.charset.Charset; + +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.openshift.api.model.ParameterBuilder; +import io.fabric8.openshift.api.model.Template; +import io.fabric8.openshift.api.model.TemplateBuilder; +import mockit.Expectations; +import mockit.Mocked; +import mockit.Verifications; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jkube.kit.resource.service.TemplateUtil.getSingletonTemplate; +import static org.eclipse.jkube.kit.resource.service.TemplateUtil.interpolateTemplateVariables; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.fail; + +@SuppressWarnings({"AccessStaticViaInstance", "ConstantConditions"}) +public class TemplateUtilTest { + + @SuppressWarnings("unused") + @Mocked + private FileUtils fileUtils; + + private KubernetesListBuilder klb; + + @Before + public void initGlobalVariables() { + klb = new KubernetesListBuilder(); + } + + @Test + public void getSingletonTemplateWithNullShouldReturnNull() { + assertThat(getSingletonTemplate(null)).isNull(); + } + + @Test + public void getSingletonTemplateWithMultipleItemsShouldReturnNull() { + // Given + klb.addToItems(new Template(), new Template()); + // When - Then + assertThat(getSingletonTemplate(klb.build())).isNull(); + } + + @Test + public void getSingletonTemplateWithSingleItemsShouldReturnTemplate() { + // Given + klb.addToItems(new TemplateBuilder().withNewMetadata().withName("template").endMetadata().build()); + // When - Then + assertThat(getSingletonTemplate(klb.build())) + .hasFieldOrPropertyWithValue("metadata.name", "template"); + } + + @Test + public void interpolateTemplateVariablesWithNoParametersShouldDoNothing() throws IOException { + // When + interpolateTemplateVariables(klb.build(), null); + // Then + verifyWriteStringToFile(0, null); + } + + @Test + public void interpolateTemplateVariablesWithParametersAndNoPlaceholdersShouldDoNothing() throws IOException { + // Given + klb.addToItems(new TemplateBuilder() + .addToParameters(new ParameterBuilder().withName("param1").withValue("value1").build()) + .build()); + mockReadFileToString("No parameters here"); + // When + interpolateTemplateVariables(klb.build(), null); + // Then + verifyWriteStringToFile(0, null); + } + + @Test + public void interpolateTemplateVariablesWithParametersAndPlaceholdersShouldReplace() throws IOException { + // Given + klb.addToItems(new TemplateBuilder() + .addToParameters(new ParameterBuilder().withName("param1").withValue("value1").build()) + .build()); + mockReadFileToString("One parameter: ${param1}, and non-existent ${oops}"); + // When + interpolateTemplateVariables(klb.build(), null); + // Then + verifyWriteStringToFile(1, "One parameter: value1, and non-existent ${oops}"); + } + + @Test + public void interpolateTemplateVariablesWithReadFileException() throws IOException { + // Given + klb.addToItems(new TemplateBuilder() + .addToParameters(new ParameterBuilder().withName("param1").withValue("value1").build()) + .build()); + mockReadFileToString(new IOException()); + // When + final IOException result = assertThrows(IOException.class, () -> { + interpolateTemplateVariables(klb.build(), null); + fail(); + }); + // Then + assertThat(result) + .isNotNull() + .hasMessage("Failed to load null for template variable replacement"); + } + + @Test + public void interpolateTemplateVariablesWithWriteFileException() throws IOException { + // Given + klb.addToItems(new TemplateBuilder() + .addToParameters(new ParameterBuilder().withName("param1").withValue("value1").build()) + .build()); + mockReadFileToString("One parameter: ${param1}, and non-existent ${oops}"); + // @formatter:off + new Expectations() {{ + fileUtils.writeStringToFile(null, anyString, Charset.defaultCharset()); + result = new IOException(); + }}; + // @formatter:on + // When + final IOException result = assertThrows(IOException.class, () -> { + interpolateTemplateVariables(klb.build(), null); + fail(); + }); + // Then + assertThat(result) + .isNotNull() + .hasMessage("Failed to save null after replacing template expressions"); + } + + private void mockReadFileToString(Object readString) throws IOException { + // @formatter:off + new Expectations() {{ + fileUtils.readFileToString(null, Charset.defaultCharset()); result = readString; + }}; + // @formatter:on + } + + private void verifyWriteStringToFile(int numTimes, String expectedString) throws IOException { + // @formatter:off + new Verifications() {{ + String s; + fileUtils.writeStringToFile(null, s = withCapture(), Charset.defaultCharset()); times = numTimes; + if (numTimes > 0) { + assertThat(s).isEqualTo(expectedString); + } + }}; + // @formatter:on + } +} diff --git a/jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/WriteUtilTest.java b/jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/WriteUtilTest.java new file mode 100644 index 0000000000..dd85134b18 --- /dev/null +++ b/jkube-kit/resource/service/src/test/java/org/eclipse/jkube/kit/resource/service/WriteUtilTest.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.resource.service; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jkube.kit.common.KitLogger; +import org.eclipse.jkube.kit.common.util.ResourceUtil; + +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.SecretBuilder; +import mockit.Expectations; +import mockit.Mocked; +import mockit.Verifications; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; + +@SuppressWarnings({"ConstantConditions", "AccessStaticViaInstance"}) +public class WriteUtilTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @SuppressWarnings("unused") + @Mocked + private KitLogger log; + @SuppressWarnings("unused") + @Mocked + private ResourceUtil resourceUtil; + + private KubernetesListBuilder klb; + private File resourceFileBase; + + @Before + public void initGlobalVariables() throws IOException { + klb = new KubernetesListBuilder(); + resourceFileBase = temporaryFolder.newFolder(); + } + + @Test + public void writeResource() throws IOException { + // Given + final File baton = temporaryFolder.newFile(); + mockResourceUtilSave(baton); + // When + final File result = WriteUtil.writeResource(null, null, null); + // Then + assertThat(result).isEqualTo(baton); + } + + @Test + public void writeResourceThrowsException() throws IOException { + // Given + final File resource = temporaryFolder.newFolder("resource-base"); + mockResourceUtilSave(new IOException("Message")); + // When + final IOException result = assertThrows(IOException.class, + () -> WriteUtil.writeResource(resource, null, null) + ); + // Then + assertThat(result) + .isNotNull() + .hasMessageStartingWith("Failed to write resource to ") + .hasMessageEndingWith("resource-base."); + } + + @Test + public void writeResourcesIndividualAndCompositeWithNoResourcesShouldOnlyWriteComposite() throws IOException { + // When + WriteUtil.writeResourcesIndividualAndComposite(klb.build(), resourceFileBase, null, log); + // Then + verifyResourceUtilSave(resourceFileBase, 1); + } + + @Test + public void writeResourcesIndividualAndCompositeWithResourcesShouldWriteAll() throws IOException { + // Given + klb.addToItems( + new ConfigMapBuilder().withNewMetadata().withName("cm-1").endMetadata().build(), + new SecretBuilder().withNewMetadata().withName("secret-1").endMetadata().build(), + new SecretBuilder().withNewMetadata().withName(" ").withClusterName("skipped (blank name)").endMetadata().build() + ); + // When + WriteUtil.writeResourcesIndividualAndComposite(klb.build(), resourceFileBase, null, log); + // Then + verifyResourceUtilSave(null, 3); + verifyResourceUtilSave(resourceFileBase, 1); + verifyResourceUtilSave(new File(resourceFileBase, "cm-1-configmap"), 1); + verifyResourceUtilSave(new File(resourceFileBase, "secret-1-secret"), 1); + } + + private void mockResourceUtilSave(Object returnValue) throws IOException { + // @formatter:off + new Expectations() {{ + resourceUtil.save((File)any, null, null); result = returnValue; + }}; + // @formatter:on + } + + private void verifyResourceUtilSave(File file, int numTimes) throws IOException { + // @formatter:off + new Verifications() {{ + String s; + resourceUtil.save(file, any, null); times = numTimes; + }}; + // @formatter:on + } +} diff --git a/kubernetes-maven-plugin/doc/src/main/asciidoc/inc/goals/build/_jkube-resource.adoc b/kubernetes-maven-plugin/doc/src/main/asciidoc/inc/goals/build/_jkube-resource.adoc index f8662258b8..af3eb1afa0 100644 --- a/kubernetes-maven-plugin/doc/src/main/asciidoc/inc/goals/build/_jkube-resource.adoc +++ b/kubernetes-maven-plugin/doc/src/main/asciidoc/inc/goals/build/_jkube-resource.adoc @@ -415,6 +415,20 @@ endif::[] Defaults to `false`. | `jkube.mergeWithDekorate` +| *interpolateTemplateParameters* +| Interpolate parameter values from `*template.yml` fragments in the generated resource list (`kubernetes.yml`). + + This is useful when using JKube in combination with Helm. + + Placeholders for variables defined in template files can be used in the different resource fragments. Helm generated + charts will contain these placeholders/parameters. + + For `resource` goal, these placeholders are replaced in the + aggregated resource list YAML file (not in the individual generated resources) if this option is enabled. + + Defaults to `true`. +| `jkube.interpolateTemplateParameters` + | *skipResource* | Skip resource generation. diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/Chart.yaml b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/Chart.yaml new file mode 100644 index 0000000000..3a91305a01 --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/Chart.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2019 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at: +# +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +--- +name: helm-and-fragments +version: "@ignore@" diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/templates/helm-and-fragments-deployment.yaml b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/templates/helm-and-fragments-deployment.yaml new file mode 100644 index 0000000000..6cb4f41d87 --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/templates/helm-and-fragments-deployment.yaml @@ -0,0 +1,70 @@ +# +# Copyright (c) 2019 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at: +# +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + jkube.io/git-url: "@ignore@" + jkube.io/git-commit: "@ignore@" + jkube.io/git-branch: "@ignore@" + labels: + app: helm-and-fragments + provider: jkube + version: "@ignore@" + group: org.eclipse.jkube + name: helm-and-fragments +spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: helm-and-fragments + provider: jkube + group: org.eclipse.jkube + template: + metadata: + annotations: + jkube.io/git-url: "@ignore@" + jkube.io/git-commit: "@ignore@" + jkube.io/git-branch: "@ignore@" + labels: + app: helm-and-fragments + provider: jkube + version: "@ignore@" + group: org.eclipse.jkube + spec: + containers: + - env: + - name: PARAMETER_ENV + value: {{ .Values.parameter_with_no_value }} + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + image: marcnuri/yakc-kubernetes-dashboard:latest + imagePullPolicy: IfNotPresent + name: helm-and-fragments + ports: + - containerPort: 8080 + name: http + protocol: TCP + resources: + limits: + memory: {{ .Values.limits_memory | default "512Mi" }} + requests: + memory: {{ .Values.requests_memory | default "256Mi" }} + securityContext: + privileged: false diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/values.yaml b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/values.yaml new file mode 100644 index 0000000000..18dcbedbb7 --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/helm/values.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2019 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at: +# +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +--- +limits_memory: 512Mi +requests_memory: 256Mi diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/resource/kubernetes.yml b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/resource/kubernetes.yml new file mode 100644 index 0000000000..82a70e908b --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/resource/kubernetes.yml @@ -0,0 +1,97 @@ +# +# Copyright (c) 2019 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at: +# +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +--- +apiVersion: v1 +kind: List +items: +- apiVersion: apps/v1 + kind: Deployment + metadata: + annotations: + jkube.io/git-url: "@ignore@" + jkube.io/git-commit: "@ignore@" + jkube.io/git-branch: "@ignore@" + labels: + app: helm-and-fragments + provider: jkube + version: "@ignore@" + group: org.eclipse.jkube + name: helm-and-fragments + spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: helm-and-fragments + provider: jkube + group: org.eclipse.jkube + template: + metadata: + annotations: + jkube.io/git-url: "@ignore@" + jkube.io/git-commit: "@ignore@" + jkube.io/git-branch: "@ignore@" + labels: + app: helm-and-fragments + provider: jkube + version: "@ignore@" + group: org.eclipse.jkube + spec: + containers: + - env: + - name: PARAMETER_ENV + value: ${parameter_with_no_value} + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + image: marcnuri/yakc-kubernetes-dashboard:latest + imagePullPolicy: IfNotPresent + name: helm-and-fragments + ports: + - containerPort: 8080 + name: http + protocol: TCP + resources: + limits: + memory: 512Mi + requests: + memory: 256Mi + securityContext: + privileged: false +- apiVersion: v1 + kind: Template + metadata: + labels: + app: helm-and-fragments + provider: jkube + version: "@ignore@" + group: org.eclipse.jkube + name: first + parameters: + - name: requests_memory + value: 256Mi +- apiVersion: v1 + kind: Template + metadata: + labels: + app: helm-and-fragments + provider: jkube + version: "@ignore@" + group: org.eclipse.jkube + name: helm-and-fragments + parameters: + - name: limits_memory + value: 512Mi diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/resource/kubernetes/helm-and-fragments-deployment.yml b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/resource/kubernetes/helm-and-fragments-deployment.yml new file mode 100644 index 0000000000..b883c03a84 --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/expected/resource/kubernetes/helm-and-fragments-deployment.yml @@ -0,0 +1,70 @@ +# +# Copyright (c) 2019 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at: +# +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + jkube.io/git-url: "@ignore@" + jkube.io/git-commit: "@ignore@" + jkube.io/git-branch: "@ignore@" + labels: + app: helm-and-fragments + provider: jkube + version: "@ignore@" + group: org.eclipse.jkube + name: helm-and-fragments +spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: helm-and-fragments + provider: jkube + group: org.eclipse.jkube + template: + metadata: + annotations: + jkube.io/git-url: "@ignore@" + jkube.io/git-commit: "@ignore@" + jkube.io/git-branch: "@ignore@" + labels: + app: helm-and-fragments + provider: jkube + version: "@ignore@" + group: org.eclipse.jkube + spec: + containers: + - env: + - name: PARAMETER_ENV + value: ${parameter_with_no_value} + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + image: marcnuri/yakc-kubernetes-dashboard:latest + imagePullPolicy: IfNotPresent + name: helm-and-fragments + ports: + - containerPort: 8080 + name: http + protocol: TCP + resources: + limits: + memory: ${limits_memory} + requests: + memory: ${requests_memory} + securityContext: + privileged: false diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/invoker.properties b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/invoker.properties new file mode 100644 index 0000000000..f0e13b33d1 --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/invoker.properties @@ -0,0 +1,16 @@ +# +# Copyright (c) 2019 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at: +# +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +invoker.goals.1=clean k8s:resource k8s:helm +invoker.debug=false diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/pom.xml b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/pom.xml new file mode 100644 index 0000000000..1ad37a1f20 --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/pom.xml @@ -0,0 +1,37 @@ + + + + + 4.0.0 + + helm-and-fragments + org.eclipse.jkube + 0.1-SNAPSHOT + jar + + + + + + org.eclipse.jkube + kubernetes-maven-plugin + @jkube.version@ + + + + + diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/deployment.yml b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/deployment.yml new file mode 100644 index 0000000000..38c46b43c0 --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/deployment.yml @@ -0,0 +1,37 @@ +# +# Copyright (c) 2019 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at: +# +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: apps/v1 +kind: Deployment +spec: + template: + spec: + containers: + - env: + - name: PARAMETER_ENV + value: ${parameter_with_no_value} + image: marcnuri/yakc-kubernetes-dashboard:latest + imagePullPolicy: IfNotPresent + name: helm-and-fragments + ports: + - containerPort: 8080 + name: http + protocol: TCP + securityContext: + privileged: false + resources: + limits: + memory: ${limits_memory} + requests: + memory: ${requests_memory} \ No newline at end of file diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/first-template.yml b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/first-template.yml new file mode 100644 index 0000000000..30244721ef --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/first-template.yml @@ -0,0 +1,18 @@ +# +# Copyright (c) 2019 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at: +# +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +kind: Template +parameters: + - name: requests_memory + value: "256Mi" \ No newline at end of file diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/template.yml b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/template.yml new file mode 100644 index 0000000000..7aa2b6e1e7 --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/src/main/jkube/template.yml @@ -0,0 +1,19 @@ +# +# Copyright (c) 2019 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at: +# +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +kind: Template +parameters: + - name: limits_memory + value: "512Mi" + - name: parameter_with_no_value diff --git a/kubernetes-maven-plugin/it/src/it/helm-and-fragments/verify.groovy b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/verify.groovy new file mode 100644 index 0000000000..066ddb9337 --- /dev/null +++ b/kubernetes-maven-plugin/it/src/it/helm-and-fragments/verify.groovy @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +import org.eclipse.jkube.maven.it.Verify + +// Verify k8s:resource output +["kubernetes", "kubernetes/helm-and-fragments-deployment"].each { + Verify.verifyResourceDescriptors( + new File(basedir, sprintf("/target/classes/META-INF/jkube/%s.yml", it)), + new File(basedir, sprintf("/expected/resource/%s.yml", it))) +} + +// Verify k8s:helm output +["templates/helm-and-fragments-deployment"].each { + Verify.verifyResourceDescriptors( + new File(basedir, sprintf("/target/jkube/helm/helm-and-fragments/kubernetes/%s.yaml", it)), + new File(basedir, sprintf("/expected/helm/%s.yaml", it))) +} + +true diff --git a/kubernetes-maven-plugin/it/src/test/java/org/eclipse/jkube/maven/it/Verify.java b/kubernetes-maven-plugin/it/src/test/java/org/eclipse/jkube/maven/it/Verify.java index c07a028e23..9b4ce67318 100644 --- a/kubernetes-maven-plugin/it/src/test/java/org/eclipse/jkube/maven/it/Verify.java +++ b/kubernetes-maven-plugin/it/src/test/java/org/eclipse/jkube/maven/it/Verify.java @@ -90,7 +90,11 @@ public static JSONObject newMessage(String txt) throws ParseException, IOExcepti } public static String asJson(String txt) throws IOException { - Object obj = new ObjectMapper(new YAMLFactory()).readValue(txt, Object.class); + Object obj = new ObjectMapper(new YAMLFactory()).readValue(jsonCompatibleYaml(txt), Object.class); return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(obj); } + + public static String jsonCompatibleYaml(String txt) { + return txt.replace("{{", "{").replace("}}", "}"); + } } \ No newline at end of file diff --git a/kubernetes-maven-plugin/plugin/pom.xml b/kubernetes-maven-plugin/plugin/pom.xml index 17ffc3e700..38759bf664 100644 --- a/kubernetes-maven-plugin/plugin/pom.xml +++ b/kubernetes-maven-plugin/plugin/pom.xml @@ -64,6 +64,12 @@ ${jkube.kit.version} + + org.eclipse.jkube + jkube-kit-resource-service + ${jkube.kit.version} + + org.eclipse.jkube jkube-kit-resource-helm @@ -123,7 +129,7 @@ jkube-kit-vertx ${jkube.kit.version} - + org.eclipse.jkube jkube-kit-wildfly-jar diff --git a/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/enricher/EnricherManager.java b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/enricher/DefaultEnricherManager.java similarity index 85% rename from kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/enricher/EnricherManager.java rename to kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/enricher/DefaultEnricherManager.java index 5823d1c26c..aeb30b8a3b 100644 --- a/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/enricher/EnricherManager.java +++ b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/enricher/DefaultEnricherManager.java @@ -19,6 +19,7 @@ import org.eclipse.jkube.kit.common.util.PluginServiceFactory; import org.eclipse.jkube.kit.config.resource.PlatformMode; import org.eclipse.jkube.kit.config.resource.ProcessorConfig; +import org.eclipse.jkube.kit.config.service.EnricherManager; import org.eclipse.jkube.kit.enricher.api.Enricher; import org.eclipse.jkube.kit.enricher.api.EnricherContext; @@ -31,17 +32,17 @@ /** * @author roland */ -public class EnricherManager { +public class DefaultEnricherManager implements EnricherManager { // List of enrichers used for customizing the generated deployment descriptors - private List enrichers; + private final List enrichers; // context used by enrichers private final ProcessorConfig defaultEnricherConfig; - private KitLogger log; + private final KitLogger log; - public EnricherManager(EnricherContext enricherContext, Optional> extraClasspathElements) { + public DefaultEnricherManager(EnricherContext enricherContext, Optional> extraClasspathElements) { PluginServiceFactory pluginFactory = new PluginServiceFactory<>(enricherContext); extraClasspathElements.ifPresent( @@ -59,10 +60,12 @@ public EnricherManager(EnricherContext enricherContext, Optional> e logEnrichers(filterEnrichers(defaultEnricherConfig, enrichers)); } + @Override public void createDefaultResources(PlatformMode platformMode, final KubernetesListBuilder builder) { createDefaultResources(platformMode, defaultEnricherConfig, builder); } + @Override public void createDefaultResources(PlatformMode platformMode, ProcessorConfig enricherConfig, final KubernetesListBuilder builder) { // Add default resources loop(enricherConfig, enricher -> { @@ -71,21 +74,18 @@ public void createDefaultResources(PlatformMode platformMode, ProcessorConfig en }); } + @Override public void enrich(PlatformMode platformMode, KubernetesListBuilder builder) { enrich(platformMode, defaultEnricherConfig, builder); } - public void enrich(PlatformMode platformMode, ProcessorConfig config, KubernetesListBuilder builder) { - enrich(platformMode, config, builder, enrichers); - } - /** * Allow enricher to add Metadata to the resources. * * @param builder builder to customize - * @param enricherList list of enrichers */ - private void enrich(PlatformMode platformMode, final ProcessorConfig enricherConfig, final KubernetesListBuilder builder, final List enricherList) { + @Override + public void enrich(PlatformMode platformMode, final ProcessorConfig enricherConfig, final KubernetesListBuilder builder) { loop(enricherConfig, enricher -> { enricher.enrich(platformMode, builder); return null; diff --git a/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/AbstractDockerMojo.java b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/AbstractDockerMojo.java index 0de55cc9d0..167df583d4 100644 --- a/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/AbstractDockerMojo.java +++ b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/AbstractDockerMojo.java @@ -70,7 +70,7 @@ import org.eclipse.jkube.kit.profile.ProfileUtil; import org.eclipse.jkube.kit.enricher.api.EnricherContext; import org.eclipse.jkube.kit.enricher.api.JKubeEnricherContext; -import org.eclipse.jkube.maven.plugin.enricher.EnricherManager; +import org.eclipse.jkube.maven.plugin.enricher.DefaultEnricherManager; import org.eclipse.jkube.maven.plugin.generator.GeneratorManager; import org.apache.maven.artifact.DependencyResolutionRequiredException; @@ -675,7 +675,7 @@ protected BuildServiceConfig.BuildServiceConfigBuilder buildServiceConfigBuilder } }) .enricherTask(builder -> { - EnricherManager enricherManager = new EnricherManager(getEnricherContext(), MavenUtil.getCompileClasspathElementsIfRequested(project, useProjectClasspath)); + DefaultEnricherManager enricherManager = new DefaultEnricherManager(getEnricherContext(), MavenUtil.getCompileClasspathElementsIfRequested(project, useProjectClasspath)); enricherManager.enrich(PlatformMode.kubernetes, builder); enricherManager.enrich(PlatformMode.openshift, builder); }); diff --git a/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/AbstractJKubeMojo.java b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/AbstractJKubeMojo.java index 2b7e219b01..0a49febbca 100644 --- a/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/AbstractJKubeMojo.java +++ b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/AbstractJKubeMojo.java @@ -77,15 +77,7 @@ protected void init() throws DependencyResolutionRequiredException { log = createLogger(null); clusterAccess = new ClusterAccess(log, initClusterConfiguration()); final JavaProject javaProject = MavenUtil.convertMavenProjectToJKubeProject(project, session); - jkubeServiceHub = JKubeServiceHub.builder() - .log(log) - .configuration(JKubeConfiguration.builder() - .project(javaProject) - .reactorProjects(Collections.singletonList(javaProject)) - .build()) - .clusterAccess(clusterAccess) - .platformMode(getRuntimeMode()) - .build(); + jkubeServiceHub = initJKubeServiceHubBuilder(javaProject).build(); } protected boolean canExecute() { @@ -145,5 +137,16 @@ protected ClusterConfiguration initClusterConfiguration() { return ClusterConfiguration.from(access, System.getProperties(), project.getProperties()).build(); } + protected JKubeServiceHub.JKubeServiceHubBuilder initJKubeServiceHubBuilder(JavaProject javaProject) { + return JKubeServiceHub.builder() + .log(log) + .configuration(JKubeConfiguration.builder() + .project(javaProject) + .reactorProjects(Collections.singletonList(javaProject)) + .build()) + .clusterAccess(clusterAccess) + .platformMode(getRuntimeMode()); + } + } diff --git a/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/ResourceMojo.java b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/ResourceMojo.java index 58da189ee6..9c82db24eb 100644 --- a/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/ResourceMojo.java +++ b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/ResourceMojo.java @@ -13,42 +13,49 @@ */ package org.eclipse.jkube.maven.plugin.mojo.build; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.KubernetesListBuilder; -import io.fabric8.openshift.api.model.Template; -import org.apache.commons.io.FileUtils; -import org.apache.maven.artifact.DependencyResolutionRequiredException; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.validation.ConstraintViolationException; + import org.eclipse.jkube.generator.api.GeneratorContext; import org.eclipse.jkube.kit.build.api.helper.DockerFileUtil; -import org.eclipse.jkube.kit.common.JavaProject; -import org.eclipse.jkube.kit.config.image.ImageConfiguration; -import org.eclipse.jkube.kit.build.service.docker.helper.ConfigHelper; import org.eclipse.jkube.kit.build.service.docker.config.handler.ImageConfigResolver; +import org.eclipse.jkube.kit.build.service.docker.helper.ConfigHelper; import org.eclipse.jkube.kit.build.service.docker.helper.ImageNameFormatter; +import org.eclipse.jkube.kit.common.JavaProject; import org.eclipse.jkube.kit.common.KitLogger; import org.eclipse.jkube.kit.common.ResourceFileType; import org.eclipse.jkube.kit.common.util.EnvUtil; -import org.eclipse.jkube.kit.common.util.JKubeProjectUtil; -import org.eclipse.jkube.kit.common.util.KubernetesHelper; +import org.eclipse.jkube.kit.common.util.LazyBuilder; import org.eclipse.jkube.kit.common.util.MavenUtil; import org.eclipse.jkube.kit.common.util.ResourceClassifier; import org.eclipse.jkube.kit.common.util.ResourceUtil; -import org.eclipse.jkube.kit.common.util.ValidationUtil; import org.eclipse.jkube.kit.common.util.validator.ResourceValidator; +import org.eclipse.jkube.kit.config.image.ImageConfiguration; import org.eclipse.jkube.kit.config.image.build.JKubeBuildStrategy; import org.eclipse.jkube.kit.config.resource.MappingConfig; import org.eclipse.jkube.kit.config.resource.PlatformMode; import org.eclipse.jkube.kit.config.resource.ProcessorConfig; import org.eclipse.jkube.kit.config.resource.ResourceConfig; import org.eclipse.jkube.kit.config.resource.RuntimeMode; -import org.eclipse.jkube.kit.profile.Profile; -import org.eclipse.jkube.kit.profile.ProfileUtil; +import org.eclipse.jkube.kit.config.service.JKubeServiceHub; +import org.eclipse.jkube.kit.config.service.ResourceServiceConfig; import org.eclipse.jkube.kit.enricher.api.JKubeEnricherContext; import org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil; -import org.eclipse.jkube.maven.plugin.enricher.EnricherManager; +import org.eclipse.jkube.kit.profile.ProfileUtil; +import org.eclipse.jkube.kit.resource.service.DefaultResourceService; +import org.eclipse.jkube.maven.plugin.enricher.DefaultEnricherManager; import org.eclipse.jkube.maven.plugin.generator.GeneratorManager; -import org.apache.commons.lang3.StringUtils; + +import io.fabric8.kubernetes.api.model.KubernetesList; +import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; @@ -60,19 +67,7 @@ import org.apache.maven.shared.filtering.MavenFileFilter; import org.apache.maven.shared.filtering.MavenFilteringException; -import javax.validation.ConstraintViolationException; -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - import static org.eclipse.jkube.kit.common.ResourceFileType.yaml; -import static org.eclipse.jkube.kit.common.util.KubernetesHelper.listResourceFragments; import static org.eclipse.jkube.kit.common.util.ResourceMojoUtil.DEFAULT_RESOURCE_LOCATION; import static org.eclipse.jkube.kit.common.util.ResourceMojoUtil.useDekorate; import static org.eclipse.jkube.maven.plugin.mojo.build.BuildMojo.CONTEXT_KEY_BUILD_TIMESTAMP; @@ -198,83 +193,23 @@ public class ResourceMojo extends AbstractJKubeMojo { @Parameter(property = "jkube.mergeWithDekorate", defaultValue = "false") private Boolean mergeWithDekorate; + @Parameter(property="jkube.interpolateTemplateParameters", defaultValue = "true") + private Boolean interpolateTemplateParameters; + @Component private MavenProjectHelper projectHelper; // resourceDir when environment has been applied private File realResourceDir; - /** - * Returns the Template if the list contains a single Template only otherwise returns null - */ - protected static Template getSingletonTemplate(KubernetesList resources) { - // if the list contains a single Template lets unwrap it - if (resources != null) { - List items = resources.getItems(); - if (items != null && items.size() == 1) { - HasMetadata singleEntity = items.get(0); - if (singleEntity instanceof Template) { - return (Template) singleEntity; - } - } - } - return null; - } - - public static File writeResourcesIndividualAndComposite(KubernetesList resources, File resourceFileBase, - ResourceFileType resourceFileType, KitLogger log) throws MojoExecutionException { - - // entity is object which will be sent to writeResource for openshift.yml - // if generateRoute is false, this will be set to resources with new list - // otherwise it will be set to resources with old list. - Object entity = resources; - - // if the list contains a single Template lets unwrap it - // in resources already new or old as per condition is set. - // no need to worry about this for dropping Route. - Template template = getSingletonTemplate(resources); - if (template != null) { - entity = template; - } - - File file = writeResource(resourceFileBase, entity, resourceFileType); - // write separate files, one for each resource item - // resources passed to writeIndividualResources is also new one. - writeIndividualResources(resources, resourceFileBase, resourceFileType, log); - return file; - } - - private static void writeIndividualResources(KubernetesList resources, File targetDir, - ResourceFileType resourceFileType, KitLogger log) throws MojoExecutionException { - for (HasMetadata item : resources.getItems()) { - String name = KubernetesHelper.getName(item); - if (StringUtils.isBlank(name)) { - log.error("No name for generated item %s", item); - continue; - } - String itemFile = KubernetesResourceUtil.getNameWithSuffix(name, item.getKind()); - - // Here we are writing individual file for all the resources. - File itemTarget = new File(targetDir, itemFile); - writeResource(itemTarget, item, resourceFileType); - } - } - - private static File writeResource(File resourceFileBase, Object entity, ResourceFileType resourceFileType) - throws MojoExecutionException { - try { - return ResourceUtil.save(resourceFileBase, entity, resourceFileType); - } catch (IOException e) { - throw new MojoExecutionException("Failed to write resource to " + resourceFileBase + ". " + e, e); - } - } @Override protected boolean canExecute() { return super.canExecute() && !skipResource; } + @Override public void executeInternal() throws MojoExecutionException, MojoFailureException { if (useDekorate(project) && mergeWithDekorate) { log.info("Dekorate detected, merging JKube and Dekorate resources"); @@ -286,7 +221,6 @@ public void executeInternal() throws MojoExecutionException, MojoFailureExceptio return; } - realResourceDir = ResourceUtil.getFinalResourceDir(resourceDir, environment); updateKindFilenameMappings(); try { lateInit(); @@ -294,27 +228,47 @@ public void executeInternal() throws MojoExecutionException, MojoFailureExceptio resolvedImages = getResolvedImages(images, log); if (!skip && (!isPomProject() || hasJKubeDir())) { // Extract and generate resources which can be a mix of Kubernetes and OpenShift resources - final PlatformMode platformMode = getPlatformMode(); final ResourceClassifier resourceClassifier = getResourceClassifier(); - final KubernetesList resourceList = generateResources(platformMode, resolvedImages); - File resourceClassifierDir = new File(this.targetDir, resourceClassifier.getValue()); + final KubernetesList resourceList = generateResources(); + final File resourceClassifierDir = new File(this.targetDir, resourceClassifier.getValue()); validateIfRequired(resourceClassifierDir, resourceClassifier); - writeResources(resourceList, resourceClassifier); + final File artifact = jkubeServiceHub.getResourceService().writeResources(resourceList, resourceClassifier, log); + // Attach it to the Maven reactor so that it will also get deployed + projectHelper.attachArtifact(project, this.resourceFileType.getArtifactType(), resourceClassifier.getValue(), artifact); } } catch (IOException | DependencyResolutionRequiredException e) { throw new MojoExecutionException("Failed to generate kubernetes descriptor", e); } } - protected PlatformMode getPlatformMode() { - return PlatformMode.kubernetes; - } - @Override protected RuntimeMode getRuntimeMode() { return RuntimeMode.KUBERNETES; } + @Override + protected JKubeServiceHub.JKubeServiceHubBuilder initJKubeServiceHubBuilder(JavaProject javaProject) { + realResourceDir = ResourceUtil.getFinalResourceDir(resourceDir, environment); + if (namespace != null && !namespace.isEmpty()) { + resources = ResourceConfig.toBuilder(resources).namespace(namespace).build(); + } + final ResourceServiceConfig resourceServiceConfig = ResourceServiceConfig.builder() + .project(javaProject) + .resourceDir(realResourceDir) + .targetDir(targetDir) + .resourceFileType(resourceFileType) + .resourceConfig(resources) + .resourceFilesProcessor(resourceFiles -> mavenFilterFiles(resourceFiles, workDir)) + .interpolateTemplateParameters(interpolateTemplateParameters) + .build(); + return super.initJKubeServiceHubBuilder(javaProject) + .resourceService(new LazyBuilder<>(() -> new DefaultResourceService(resourceServiceConfig))); + } + + protected PlatformMode getPlatformMode() { + return PlatformMode.kubernetes; + } + protected ResourceClassifier getResourceClassifier() { return ResourceClassifier.KUBERNETES; } @@ -374,97 +328,21 @@ private void lateInit() { } } - private KubernetesList generateResources(PlatformMode platformMode, List images) - throws IOException, MojoExecutionException, DependencyResolutionRequiredException { + private KubernetesList generateResources() + throws IOException, DependencyResolutionRequiredException { - if (namespace != null && !namespace.isEmpty()) { - resources = ResourceConfig.toBuilder(resources).namespace(namespace).build(); - } - // Manager for calling enrichers. - JavaProject jkubeProject = MavenUtil.convertMavenProjectToJKubeProject(project, session); JKubeEnricherContext.JKubeEnricherContextBuilder ctxBuilder = JKubeEnricherContext.builder() - .project(jkubeProject) + .project(MavenUtil.convertMavenProjectToJKubeProject(project, session)) .processorConfig(extractEnricherConfig()) .settings(MavenUtil.getRegistryServerFromMavenSettings(settings)) .resources(resources) .images(resolvedImages) .log(log); - EnricherManager enricherManager = new EnricherManager(ctxBuilder.build(), + DefaultEnricherManager enricherManager = new DefaultEnricherManager(ctxBuilder.build(), MavenUtil.getCompileClasspathElementsIfRequested(project, useProjectClasspath)); - // Generate all resources from the main resource directory, configuration and create them accordingly - KubernetesListBuilder builder = generateAppResources(platformMode, images, enricherManager); - - // Add resources found in subdirectories of resourceDir, with a certain profile - // applied - addProfiledResourcesFromSubdirectories(platformMode, builder, realResourceDir, enricherManager); - return builder.build(); - } - - private void addProfiledResourcesFromSubdirectories(PlatformMode platformMode, KubernetesListBuilder builder, File resourceDir, - EnricherManager enricherManager) throws IOException, MojoExecutionException { - File[] profileDirs = resourceDir.listFiles(File::isDirectory); - if (profileDirs != null) { - for (File profileDir : profileDirs) { - Profile foundProfile = ProfileUtil.findProfile(profileDir.getName(), resourceDir); - ProcessorConfig enricherConfig = foundProfile.getEnricherConfig(); - File[] resourceFiles = listResourceFragments(profileDir); - if (resourceFiles.length > 0) { - KubernetesListBuilder profileBuilder = readResourceFragments(platformMode, resourceFiles); - enricherManager.createDefaultResources(platformMode, enricherConfig, profileBuilder); - enricherManager.enrich(platformMode, enricherConfig, profileBuilder); - KubernetesList profileItems = profileBuilder.build(); - for (HasMetadata item : profileItems.getItems()) { - builder.addToItems(item); - } - } - } - } - } - - private KubernetesListBuilder generateAppResources(PlatformMode platformMode, List images, EnricherManager enricherManager) - throws IOException, MojoExecutionException { - try { - KubernetesListBuilder builder = processResourceFragments(platformMode); - - // Create default resources for app resources only - enricherManager.createDefaultResources(platformMode, builder); - - // Enrich descriptors - enricherManager.enrich(platformMode, builder); - - return builder; - } catch (ConstraintViolationException e) { - String message = ValidationUtil.createValidationMessage(e.getConstraintViolations()); - log.error("ConstraintViolationException: %s", message); - throw new MojoExecutionException(message, e); - } - } - - private KubernetesListBuilder processResourceFragments(PlatformMode platformMode) throws IOException, MojoExecutionException { - File[] resourceFiles = listResourceFragments(realResourceDir, resources !=null ? resources.getRemotes() : null, log); - KubernetesListBuilder builder; - - // Add resource files found in the jkube directory - if (resourceFiles != null && resourceFiles.length > 0) { - log.info("using resource templates from %s", realResourceDir); - builder = readResourceFragments(platformMode, resourceFiles); - } else { - builder = new KubernetesListBuilder(); - } - return builder; - } - - private KubernetesListBuilder readResourceFragments(PlatformMode platformMode, File[] resourceFiles) throws IOException, MojoExecutionException { - KubernetesListBuilder builder; - String defaultName = JKubeProjectUtil.createDefaultResourceName(project.getArtifactId()); - builder = KubernetesResourceUtil.readResourceFragmentsFrom( - platformMode, - KubernetesResourceUtil.DEFAULT_RESOURCE_VERSIONING, - defaultName, - mavenFilterFiles(resourceFiles, this.workDir)); - return builder; + return jkubeServiceHub.getResourceService().generateResources(getPlatformMode(), enricherManager, log); } private ProcessorConfig extractEnricherConfig() throws IOException { @@ -537,11 +415,12 @@ private Date getBuildReferenceDate() throws MojoExecutionException { } } - private File[] mavenFilterFiles(File[] resourceFiles, File outDir) throws MojoExecutionException { - if (!outDir.exists()) { - if (!outDir.mkdirs()) { - throw new MojoExecutionException("Cannot create working dir " + outDir); - } + private File[] mavenFilterFiles(File[] resourceFiles, File outDir) throws IOException { + if (resourceFiles == null) { + return new File[0]; + } + if (!outDir.exists() && !outDir.mkdirs()) { + throw new IOException("Cannot create working dir " + outDir); } File[] ret = new File[resourceFiles.length]; int i = 0; @@ -552,7 +431,7 @@ private File[] mavenFilterFiles(File[] resourceFiles, File outDir) throws MojoEx project, null, false, "utf8", session); ret[i++] = targetFile; } catch (MavenFilteringException exp) { - throw new MojoExecutionException( + throw new IOException( String.format("Cannot filter %s to %s", resource, targetFile), exp); } } @@ -567,64 +446,4 @@ private boolean isPomProject() { return "pom".equals(project.getPackaging()); } - protected void writeResources(KubernetesList resources, ResourceClassifier classifier) - throws MojoExecutionException { - // write kubernetes.yml / openshift.yml - File resourceFileBase = new File(this.targetDir, classifier.getValue()); - - File file = - writeResourcesIndividualAndComposite(resources, resourceFileBase, this.resourceFileType, log); - // Resolve template placeholders - resolveTemplateVariablesIfAny(resources, resourceFileBase, file); - - // Attach it to the Maven reactor so that it will also get deployed - projectHelper.attachArtifact(project, this.resourceFileType.getArtifactType(), classifier.getValue(), file); - } - - private void resolveTemplateVariablesIfAny(KubernetesList resources, File kubernetesResourceDir, File resourceYaml) throws MojoExecutionException { - Template template = findTemplate(resources); - if (template != null) { - List parameters = template.getParameters(); - if (parameters == null || parameters.isEmpty()) { - return; - } - resolveTemplateVariables(parameters, resourceYaml); - File[] kubernetesResources = kubernetesResourceDir.listFiles((dir, filename) -> filename.endsWith(".yml")); - if (kubernetesResources != null) { - for (File kubernetesResource : kubernetesResources) { - resolveTemplateVariables(parameters, kubernetesResource); - } - } - } - } - - private void resolveTemplateVariables(List parameters, File kubernetesYaml) throws MojoExecutionException { - String text; - try { - text = FileUtils.readFileToString(kubernetesYaml, Charset.defaultCharset()); - } catch (IOException e) { - throw new MojoExecutionException("Failed to load " + kubernetesYaml + " so we can replace template expressions " +e, e); - } - String original = text; - for (io.fabric8.openshift.api.model.Parameter parameter : parameters) { - String from = "${" + parameter.getName() + "}"; - String to = parameter.getValue(); - text = text.replace(from, to); - } - if (!original.equals(text)) { - try { - FileUtils.writeStringToFile(kubernetesYaml, text, Charset.defaultCharset()); - } catch (IOException e) { - throw new MojoExecutionException("Failed to save " + kubernetesYaml + " after replacing template expressions " +e, e); - } - } - } - - private Template findTemplate(KubernetesList resources) { - return (Template) resources.getItems().stream() - .filter(template -> template instanceof Template) - .findFirst() - .orElse(null); - } - }