This is an automated email from the ASF dual-hosted git repository. cdeppisch pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
commit a103a902cfa7b13f219268b149177f3ef63c57ed Author: Thomas Diesler <tdies...@redhat.com> AuthorDate: Tue Jul 16 20:11:57 2024 +0200 [CAMEL-20962] camel-jbang: SpringBoot support in Kubernetes plugin --- .../dsl/jbang/core/commands/ExportBaseCommand.java | 72 +++++----- .../dsl/jbang/core/commands/ExportQuarkus.java | 3 +- .../dsl/jbang/core/commands/ExportSpringBoot.java | 21 +-- ...ot-pom.tmpl => spring-boot-kubernetes-pom.tmpl} | 50 ++++--- .../main/resources/templates/spring-boot-pom.tmpl | 2 - .../jbang/core/commands/SpringBootExportTest.java | 72 ++++++++++ .../camel-jbang-core/src/test/resources/route.yaml | 23 ++++ .../core/commands/kubernetes/KubernetesExport.java | 91 +++++++------ .../commands/kubernetes/KubernetesExportTest.java | 150 +++++++++++++++++---- 9 files changed, 356 insertions(+), 128 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java index 2b61badb1db..52a044fc207 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java @@ -220,7 +220,9 @@ public abstract class ExportBaseCommand extends CamelCommand { description = "Whether to ignore route loading and compilation errors (use this with care!)") protected boolean ignoreLoadingError; - protected boolean symbolicLink; // copy source files using symbolic link + protected boolean symbolicLink; // copy source files using symbolic link + + public String pomTemplateName; // support for specialised pom templates public ExportBaseCommand(CamelJBangMain main) { super(main); @@ -362,45 +364,51 @@ public abstract class ExportBaseCommand extends CamelCommand { } } else if (line.startsWith("camel.jbang.dependencies=")) { String deps = StringHelper.after(line, "camel.jbang.dependencies="); - for (String d : deps.split(",")) { - answer.add(d.trim()); - if (kamelets && d.contains("org.apache.camel:camel-kamelet")) { - // include yaml-dsl and kamelet catalog if we use kamelets - answer.add("camel:yaml-dsl"); - answer.add("org.apache.camel.kamelets:camel-kamelets:" + kameletsVersion); + if (!deps.isEmpty()) { + for (String d : deps.split(",")) { + answer.add(d.trim()); + if (kamelets && d.contains("org.apache.camel:camel-kamelet")) { + // include yaml-dsl and kamelet catalog if we use kamelets + answer.add("camel:yaml-dsl"); + answer.add("org.apache.camel.kamelets:camel-kamelets:" + kameletsVersion); + } } } } else if (line.startsWith("camel.jbang.classpathFiles")) { String deps = StringHelper.after(line, "camel.jbang.classpathFiles="); - for (String d : deps.split(",")) { - // special to include local JARs in export lib folder - if (d.endsWith(".jar")) { - answer.add("lib:" + d.trim()); + if (!deps.isEmpty()) { + for (String d : deps.split(",")) { + // special to include local JARs in export lib folder + if (d.endsWith(".jar")) { + answer.add("lib:" + d.trim()); + } } } } else if (line.startsWith("camel.main.routesIncludePattern=")) { String routes = StringHelper.after(line, "camel.main.routesIncludePattern="); - for (String r : routes.split(",")) { - String ext = FileUtil.onlyExt(r, true); - if (ext != null) { - // java is moved into src/main/java and compiled during build - // for the other DSLs we need to add dependencies - if ("groovy".equals(ext)) { - answer.add("mvn:org.apache.camel:camel-groovy-dsl"); - } else if ("js".equals(ext)) { - answer.add("mvn:org.apache.camel:camel-js-dsl"); - } else if ("jsh".equals(ext)) { - answer.add("mvn:org.apache.camel:camel-jsh-dsl"); - } else if ("kts".equals(ext)) { - answer.add("mvn:org.apache.camel:camel-kotlin-dsl"); - } else if ("xml".equals(ext)) { - answer.add("mvn:org.apache.camel:camel-xml-io-dsl"); - } else if ("yaml".equals(ext)) { - answer.add("mvn:org.apache.camel:camel-yaml-dsl"); - // is it a kamelet? - ext = FileUtil.onlyExt(r, false); - if ("kamelet.yaml".equals(ext)) { - answer.add("mvn:org.apache.camel.kamelets:camel-kamelets:" + kameletsVersion); + if (!routes.isEmpty()) { + for (String r : routes.split(",")) { + String ext = FileUtil.onlyExt(r, true); + if (ext != null) { + // java is moved into src/main/java and compiled during build + // for the other DSLs we need to add dependencies + if ("groovy".equals(ext)) { + answer.add("mvn:org.apache.camel:camel-groovy-dsl"); + } else if ("js".equals(ext)) { + answer.add("mvn:org.apache.camel:camel-js-dsl"); + } else if ("jsh".equals(ext)) { + answer.add("mvn:org.apache.camel:camel-jsh-dsl"); + } else if ("kts".equals(ext)) { + answer.add("mvn:org.apache.camel:camel-kotlin-dsl"); + } else if ("xml".equals(ext)) { + answer.add("mvn:org.apache.camel:camel-xml-io-dsl"); + } else if ("yaml".equals(ext)) { + answer.add("mvn:org.apache.camel:camel-yaml-dsl"); + // is it a kamelet? + ext = FileUtil.onlyExt(r, false); + if ("kamelet.yaml".equals(ext)) { + answer.add("mvn:org.apache.camel.kamelets:camel-kamelets:" + kameletsVersion); + } } } } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java index 348bf73a3f5..9efec45e0fa 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java @@ -47,6 +47,7 @@ class ExportQuarkus extends Export { public ExportQuarkus(CamelJBangMain main) { super(main); + pomTemplateName = "quarkus-pom.tmpl"; } @Override @@ -351,7 +352,7 @@ class ExportQuarkus extends Export { private void createMavenPom(File settings, File pom, Set<String> deps) throws Exception { String[] ids = gav.split(":"); - InputStream is = ExportQuarkus.class.getClassLoader().getResourceAsStream("templates/quarkus-pom.tmpl"); + InputStream is = ExportQuarkus.class.getClassLoader().getResourceAsStream("templates/" + pomTemplateName); String context = IOHelper.loadText(is); IOHelper.close(is); diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java index a9941b44a15..36a42e9d85c 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java @@ -46,6 +46,7 @@ class ExportSpringBoot extends Export { public ExportSpringBoot(CamelJBangMain main) { super(main); + pomTemplateName = "spring-boot-pom.tmpl"; } @Override @@ -165,27 +166,27 @@ class ExportSpringBoot extends Export { // First try to load a specialized template from the catalog, if the catalog does not provide it // fallback to the template defined in camel-jbang-core String context; - InputStream template = catalog.loadResource("camel-jbang", "spring-boot-pom.tmpl"); + InputStream template = catalog.loadResource("camel-jbang", pomTemplateName); if (template != null) { context = IOHelper.loadText(template); } else { - context = readResourceTemplate("templates/spring-boot-pom.tmpl"); + context = readResourceTemplate("templates/" + pomTemplateName); } String camelVersion = catalog.getLoadedVersion(); - context = context.replaceFirst("\\{\\{ \\.GroupId }}", ids[0]); - context = context.replaceFirst("\\{\\{ \\.ArtifactId }}", ids[1]); - context = context.replaceFirst("\\{\\{ \\.Version }}", ids[2]); + context = context.replaceAll("\\{\\{ \\.GroupId }}", ids[0]); + context = context.replaceAll("\\{\\{ \\.ArtifactId }}", ids[1]); + context = context.replaceAll("\\{\\{ \\.Version }}", ids[2]); context = context.replaceAll("\\{\\{ \\.SpringBootVersion }}", springBootVersion); - context = context.replaceFirst("\\{\\{ \\.JavaVersion }}", javaVersion); - context = context.replaceFirst("\\{\\{ \\.CamelVersion }}", camelVersion); + context = context.replaceAll("\\{\\{ \\.JavaVersion }}", javaVersion); + context = context.replaceAll("\\{\\{ \\.CamelVersion }}", camelVersion); if (camelSpringBootVersion != null) { - context = context.replaceFirst("\\{\\{ \\.CamelSpringBootVersion }}", camelSpringBootVersion); + context = context.replaceAll("\\{\\{ \\.CamelSpringBootVersion }}", camelSpringBootVersion); } else { - context = context.replaceFirst("\\{\\{ \\.CamelSpringBootVersion }}", camelVersion); + context = context.replaceAll("\\{\\{ \\.CamelSpringBootVersion }}", camelVersion); } - if (additionalProperties != null) { + if (additionalProperties != null && !additionalProperties.isEmpty()) { String properties = Arrays.stream(additionalProperties.split(",")) .map(property -> { String[] keyValueProperty = property.split("="); diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-pom.tmpl b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-kubernetes-pom.tmpl similarity index 67% copy from dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-pom.tmpl copy to dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-kubernetes-pom.tmpl index 0d656b1b00f..d1c7490e43f 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-pom.tmpl +++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-kubernetes-pom.tmpl @@ -16,7 +16,6 @@ <properties> <java.version>{{ .JavaVersion }}</java.version> -{{ .jkubeProperties }} {{ .AdditionalProperties }} </properties> @@ -52,13 +51,11 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> - <dependency> <groupId>org.apache.camel.springboot</groupId> <artifactId>camel-spring-boot-starter</artifactId> </dependency> {{ .CamelDependencies }} - <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> @@ -77,26 +74,41 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> + <plugin> + <groupId>com.google.cloud.tools</groupId> + <artifactId>jib-maven-plugin</artifactId> + <configuration> + <from> + <image>eclipse-temurin:{{ .JavaVersion }}</image> + <platforms> + <platform> + <architecture>amd64</architecture> + <os>linux</os> + </platform> + <platform> + <architecture>arm64</architecture> + <os>linux</os> + </platform> + </platforms> + </from> + <to> + <image>${camel.springboot.kubernetes.image-name}</image> + </to> + <allowInsecureRegistries>${camel.springboot.container-image.insecure}</allowInsecureRegistries> + </configuration> + <executions> + <execution> + <goals> + <goal>dockerBuild</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + </plugin> </plugins> </build> <profiles> -{{ .jkubeProfiles }} - <profile> - <id>camel.debug</id> - <activation> - <property> - <name>camel.debug</name> - <value>true</value> - </property> - </activation> - <dependencies> - <dependency> - <groupId>org.apache.camel.springboot</groupId> - <artifactId>camel-debug-starter</artifactId> - </dependency> - </dependencies> - </profile> </profiles> </project> diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-pom.tmpl b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-pom.tmpl index 0d656b1b00f..843c2b47d43 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-pom.tmpl +++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-pom.tmpl @@ -52,13 +52,11 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> - <dependency> <groupId>org.apache.camel.springboot</groupId> <artifactId>camel-spring-boot-starter</artifactId> </dependency> {{ .CamelDependencies }} - <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/SpringBootExportTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/SpringBootExportTest.java new file mode 100644 index 00000000000..a014369065a --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/SpringBootExportTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.dsl.jbang.core.commands; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; + +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import picocli.CommandLine; + +class SpringBootExportTest { + + private File workingDir; + + @BeforeEach + public void setup() throws IOException { + workingDir = Files.createTempDirectory("camel-export").toFile(); + workingDir.deleteOnExit(); + } + + @Test + public void shouldGenerateSpringBootProject() throws Exception { + ExportSpringBoot command = createCommand(new String[] { "classpath:route.yaml" }, + "--gav=examples:route:1.0.0", "--dir=" + workingDir, "--quiet"); + int exit = command.doCall(); + + Assertions.assertEquals(0, exit); + Model model = readMavenModel(); + Assertions.assertEquals("examples", model.getGroupId()); + Assertions.assertEquals("route", model.getArtifactId()); + Assertions.assertEquals("1.0.0", model.getVersion()); + } + + private ExportSpringBoot createCommand(String[] files, String... args) { + ExportSpringBoot command = new ExportSpringBoot(new CamelJBangMain()); + CommandLine.populateCommand(command, "--gav=examples:route:1.0.0", "--dir=" + workingDir, "--quiet", + "--runtime=spring-boot"); + command.files = Arrays.asList(files); + return command; + } + + private Model readMavenModel() throws Exception { + File f = workingDir.toPath().resolve("pom.xml").toFile(); + Assertions.assertTrue(f.isFile(), "Not a pom.xml file: " + f); + MavenXpp3Reader mavenReader = new MavenXpp3Reader(); + Model model = mavenReader.read(new FileReader(f)); + model.setPomFile(f); + return model; + } +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/resources/route.yaml b/dsl/camel-jbang/camel-jbang-core/src/test/resources/route.yaml new file mode 100644 index 00000000000..8f13a54fda0 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/test/resources/route.yaml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +from: + uri: timer:tick + steps: + - setBody: + constant: Hello Camel !!! + - to: log:info diff --git a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java index d7bfc5a5603..4ad53f87b1d 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java +++ b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java @@ -29,6 +29,7 @@ import java.util.stream.Collectors; import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; import org.apache.camel.dsl.jbang.core.commands.Export; +import org.apache.camel.dsl.jbang.core.commands.ExportBaseCommand; import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.ContainerTrait; import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.TraitCatalog; import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.TraitContext; @@ -37,6 +38,7 @@ import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.TraitProfile; import org.apache.camel.dsl.jbang.core.common.RuntimeType; import org.apache.camel.util.StringHelper; import org.apache.camel.v1.integrationspec.Traits; +import org.apache.camel.v1.integrationspec.traits.Container; import picocli.CommandLine; import picocli.CommandLine.Command; @@ -162,28 +164,29 @@ public class KubernetesExport extends Export { // Quarkus specific properties exportProps.put("quarkus.container-image.build", "true"); + } - if (resolvedImageRegistry != null) { - exportProps.put("quarkus.container-image.registry", resolvedImageRegistry); - if (resolvedImageRegistry.startsWith("localhost")) { - exportProps.put("quarkus.container-image.insecure", "true"); - } - } + String propPrefix; + if (runtime == RuntimeType.springBoot) { + propPrefix = "camel.springboot"; + } else { + propPrefix = runtime.runtime(); + } - if (resolvedImageGroup != null) { - exportProps.put("quarkus.container-image.group", resolvedImageGroup); - } + if (resolvedImageGroup != null) { + exportProps.put("%s.container-image.group".formatted(propPrefix), resolvedImageGroup); } - additionalProperties = Optional.ofNullable(additionalProperties).orElse(""); - if (additionalProperties.isEmpty()) { - additionalProperties = exportProps.entrySet().stream() - .map(entry -> "%s=%s".formatted(entry.getKey(), entry.getValue())).collect(Collectors.joining(",")); - } else { - additionalProperties += "," + exportProps.entrySet().stream() - .map(entry -> "%s=%s".formatted(entry.getKey(), entry.getValue())).collect(Collectors.joining(",")); + if (resolvedImageRegistry != null) { + var allowInsecure = resolvedImageRegistry.startsWith("localhost"); + exportProps.put("%s.container-image.registry".formatted(propPrefix), resolvedImageRegistry); + exportProps.put("%s.container-image.insecure".formatted(propPrefix), "%b".formatted(allowInsecure)); } + additionalProperties = Optional.ofNullable(additionalProperties).map(str -> str + ",").orElse(""); + additionalProperties += exportProps.entrySet().stream() + .map(entry -> "%s=%s".formatted(entry.getKey(), entry.getValue())).collect(Collectors.joining(",")); + String projectName = getProjectName(); TraitContext context = new TraitContext(projectName, getVersion()); if (annotations != null) { @@ -193,12 +196,11 @@ public class KubernetesExport extends Export { .collect(Collectors.toMap(parts -> parts[0], parts -> parts[1]))); } - if (labels != null) { - context.addLabels(Arrays.stream(labels) - .map(item -> item.split("=")) - .filter(parts -> parts.length == 2) - .collect(Collectors.toMap(parts -> parts[0], parts -> parts[1]))); - } + labels = Optional.ofNullable(labels).orElse(new String[0]); + context.addLabels(Arrays.stream(labels) + .map(item -> item.split("=")) + .filter(parts -> parts.length == 2) + .collect(Collectors.toMap(parts -> parts[0], parts -> parts[1]))); if (traitProfile != null) { context.setProfile(TraitProfile.valueOf(traitProfile)); @@ -209,27 +211,29 @@ public class KubernetesExport extends Export { TraitHelper.configureMountTrait(traitsSpec, configs, resources, volumes); TraitHelper.configureOpenApiSpec(traitsSpec, openApis); TraitHelper.configureProperties(traitsSpec, properties); - TraitHelper.configureContainerImage(traitsSpec, image, resolvedImageRegistry, resolvedImageGroup, projectName, - getVersion()); + TraitHelper.configureContainerImage(traitsSpec, image, + resolvedImageRegistry, resolvedImageGroup, projectName, getVersion()); TraitHelper.configureEnvVars(traitsSpec, envVars); TraitHelper.configureConnects(traitsSpec, connects); - // Need to set quarkus.container properties, otherwise these settings get overwritten by Quarkus - if (RuntimeType.quarkus == runtime && traitsSpec.getContainer() != null) { - if (traitsSpec.getContainer().getName() != null && !traitsSpec.getContainer().getName().equals(projectName)) { - additionalProperties += ",quarkus.kubernetes.container-name=%s".formatted(traitsSpec.getContainer().getName()); - } - - if (traitsSpec.getContainer().getPort() != null) { - additionalProperties += ",quarkus.kubernetes.ports.%s.container-port=%s" - .formatted(Optional.ofNullable(traitsSpec.getContainer().getPortName()) - .orElse(ContainerTrait.DEFAULT_CONTAINER_PORT_NAME), traitsSpec.getContainer().getPort()); - } + Container container = traitsSpec.getContainer(); - if (traitsSpec.getContainer().getImagePullPolicy() != null) { - additionalProperties += ",quarkus.kubernetes.image-pull-policy=%s" - .formatted(StringHelper.camelCaseToDash(traitsSpec.getContainer().getImagePullPolicy().getValue())); - } + // Need to set quarkus.container properties, otherwise these settings get overwritten by Quarkus + if (container.getName() != null && !container.getName().equals(projectName)) { + additionalProperties += ",%s.kubernetes.container-name=%s".formatted(propPrefix, container.getName()); + } + if (container.getImage() != null) { + additionalProperties += ",%s.kubernetes.image-name=%s".formatted(propPrefix, container.getImage()); + } + if (container.getPort() != null) { + additionalProperties += ",%s.kubernetes.ports.%s.container-port=%s".formatted(propPrefix, + Optional.ofNullable(container.getPortName()).orElse( + ContainerTrait.DEFAULT_CONTAINER_PORT_NAME), + container.getPort()); + } + if (container.getImagePullPolicy() != null) { + additionalProperties += ",%s.kubernetes.image-pull-policy=%s" + .formatted(propPrefix, StringHelper.camelCaseToDash(container.getImagePullPolicy().getValue())); } // run export @@ -258,12 +262,19 @@ public class KubernetesExport extends Export { return 0; } + protected Integer export(ExportBaseCommand cmd) throws Exception { + if (runtime == RuntimeType.springBoot) { + cmd.pomTemplateName = "spring-boot-kubernetes-pom.tmpl"; + } + return super.export(cmd); + } + protected Traits getTraitSpec() { Traits traitsSpec; if (traits != null && traits.length > 0) { traitsSpec = TraitHelper.parseTraits(traits, annotations); } else if (annotations != null && annotations.length > 0) { - traitsSpec = TraitHelper.parseTraits(new String[] {}, annotations); + traitsSpec = TraitHelper.parseTraits(new String[0], annotations); } else { traitsSpec = new Traits(); } diff --git a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportTest.java b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportTest.java index 83accb91fa7..c1fd9711cbe 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportTest.java +++ b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportTest.java @@ -22,13 +22,12 @@ import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Optional; +import java.util.*; import io.fabric8.kubernetes.api.model.apps.Deployment; import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait; +import org.apache.camel.dsl.jbang.core.common.RuntimeType; import org.apache.maven.model.Model; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.junit.jupiter.api.Assertions; @@ -50,7 +49,17 @@ class KubernetesExportTest { @Test public void shouldGenerateQuarkusProject() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--gav=examples:route:1.0.0"); + shouldGenerateProject(RuntimeType.quarkus); + } + + @Test + public void shouldGenerateSpringBootProject() throws Exception { + shouldGenerateProject(RuntimeType.springBoot); + } + + void shouldGenerateProject(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, + "--gav=examples:route:1.0.0", "--runtime=" + rt.runtime()); int exit = command.doCall(); Assertions.assertEquals(0, exit); @@ -61,8 +70,18 @@ class KubernetesExportTest { } @Test - public void shouldGenerateKubernetesManifest() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--image-group=camel-test"); + public void shouldGenerateQuarkusKubernetesManifest() throws Exception { + shouldGenerateKubernetesManifest(RuntimeType.quarkus); + } + + @Test + public void shouldGenerateSpringBootKubernetesManifest() throws Exception { + shouldGenerateKubernetesManifest(RuntimeType.springBoot); + } + + void shouldGenerateKubernetesManifest(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, + "--image-group=camel-test", "--runtime=" + rt.runtime()); int exit = command.doCall(); Assertions.assertEquals(0, exit); @@ -79,8 +98,18 @@ class KubernetesExportTest { } @Test - public void shouldAddContainerSpec() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--gav=camel-test:route:1.0.0"); + public void shouldAddQuarkusContainerSpec() throws Exception { + shouldAddContainerSpec(RuntimeType.quarkus); + } + + @Test + public void shouldAddSpringBootContainerSpec() throws Exception { + shouldAddContainerSpec(RuntimeType.springBoot); + } + + void shouldAddContainerSpec(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, + "--gav=camel-test:route:1.0.0", "--runtime=" + rt.runtime()); command.traits = new String[] { "container.port=8088", "container.image-pull-policy=IfNotPresent" }; command.doCall(); @@ -99,8 +128,17 @@ class KubernetesExportTest { } @Test - public void shouldAddVolumes() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }); + public void shouldAddQuarkusVolumes() throws Exception { + shouldAddVolumes(RuntimeType.quarkus); + } + + @Test + public void shouldAddSpringBootVolumes() throws Exception { + shouldAddVolumes(RuntimeType.springBoot); + } + + void shouldAddVolumes(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--runtime=" + rt.runtime()); command.volumes = new String[] { "pvc-foo:/container/path/foo", "pvc-bar:/container/path/bar" }; command.doCall(); @@ -127,8 +165,17 @@ class KubernetesExportTest { } @Test - public void shouldAddEnvVars() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }); + public void shouldAddQuarkusEnvVars() throws Exception { + shouldAddEnvVars(RuntimeType.quarkus); + } + + @Test + public void shouldAddSpringBootEnvVars() throws Exception { + shouldAddEnvVars(RuntimeType.springBoot); + } + + void shouldAddEnvVars(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--runtime=" + rt.runtime()); command.envVars = new String[] { "CAMEL_FOO=bar", "MY_ENV=foo" }; command.doCall(); @@ -147,8 +194,17 @@ class KubernetesExportTest { } @Test - public void shouldAddAnnotations() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }); + public void shouldAddQuarkusAnnotations() throws Exception { + shouldAddAnnotations(RuntimeType.quarkus); + } + + @Test + public void shouldAddSpringBootAnnotations() throws Exception { + shouldAddAnnotations(RuntimeType.springBoot); + } + + void shouldAddAnnotations(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--runtime=" + rt.runtime()); command.annotations = new String[] { "foo=bar" }; command.doCall(); @@ -159,8 +215,18 @@ class KubernetesExportTest { } @Test - public void shouldAddLabels() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--label=foo=bar"); + public void shouldAddQuarkusLabels() throws Exception { + shouldAddLabels(RuntimeType.quarkus); + } + + @Test + public void shouldAddSpringBootLabels() throws Exception { + shouldAddLabels(RuntimeType.springBoot); + } + + void shouldAddLabels(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, + "--label=foo=bar", "--runtime=" + rt.runtime()); command.doCall(); Deployment deployment = getDeployment(workingDir); @@ -171,8 +237,17 @@ class KubernetesExportTest { } @Test - public void shouldAddConfigs() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }); + public void shouldAddQuarkusConfigs() throws Exception { + shouldAddConfigs(RuntimeType.quarkus); + } + + @Test + public void shouldAddSpringBootConfigs() throws Exception { + shouldAddConfigs(RuntimeType.springBoot); + } + + void shouldAddConfigs(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--runtime=" + rt.runtime()); command.configs = new String[] { "secret:foo", "configmap:bar" }; command.doCall(); @@ -196,8 +271,17 @@ class KubernetesExportTest { } @Test - public void shouldAddResources() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }); + public void shouldAddQuarkusResources() throws Exception { + shouldAddResources(RuntimeType.quarkus); + } + + @Test + public void shouldAddSpringBootResources() throws Exception { + shouldAddResources(RuntimeType.springBoot); + } + + void shouldAddResources(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--runtime=" + rt.runtime()); command.resources = new String[] { "configmap:foo/file.txt" }; command.doCall(); @@ -215,8 +299,17 @@ class KubernetesExportTest { } @Test - public void shouldAddOpenApis() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }); + public void shouldAddQuarkusOpenApis() throws Exception { + shouldAddOpenApis(RuntimeType.quarkus); + } + + @Test + public void shouldAddSpringBootOpenApis() throws Exception { + shouldAddOpenApis(RuntimeType.springBoot); + } + + void shouldAddOpenApis(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--runtime=" + rt.runtime()); command.openApis = new String[] { "configmap:openapi/spec.yaml" }; command.doCall(); @@ -234,8 +327,17 @@ class KubernetesExportTest { } @Test - public void shouldUseImage() throws Exception { - KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }); + public void shouldUseQuarkusImage() throws Exception { + shouldUseImage(RuntimeType.quarkus); + } + + @Test + public void shouldUseSpringBootImage() throws Exception { + shouldUseImage(RuntimeType.springBoot); + } + + public void shouldUseImage(RuntimeType rt) throws Exception { + KubernetesExport command = createCommand(new String[] { "classpath:route.yaml" }, "--runtime=" + rt.runtime()); command.image = "quay.io/camel/demo-app:1.0"; command.doCall();