This is an automated email from the ASF dual-hosted git repository. nfilotto pushed a commit to branch 3023/compile-with-sources in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 10d062bf42299ec87130e49538dbcf9e26536909 Author: Nicolas Filotto <[email protected]> AuthorDate: Wed Feb 1 13:17:45 2023 +0100 Ref #3023: Add native compilation with sources --- docs/modules/ROOT/partials/apis/camel-k-crds.adoc | 8 +- docs/modules/traits/pages/quarkus.adoc | 8 +- .../native/files/{Java.java => Groovy.groovy} | 15 +-- e2e/namespace/native/files/Java.java | 8 +- .../native/files/{Java.java => JavaScript.js} | 15 +-- .../native/files/{Java.java => JavaShell.jsh} | 15 +-- .../native/files/{Java.java => Kotlin.kts} | 17 +-- e2e/namespace/native/files/Xml.xml | 33 ++++++ e2e/namespace/native/native_test.go | 118 ++++++++++++++++++++- pkg/apis/camel/v1/common_types.go | 3 + pkg/apis/camel/v1/trait/quarkus.go | 8 +- pkg/builder/builder.go | 19 ++-- pkg/builder/quarkus.go | 63 ++++++++++- pkg/builder/types.go | 1 + pkg/resources/resources.go | 8 +- pkg/trait/camel.go | 2 +- pkg/trait/quarkus.go | 73 ++++++++++++- pkg/trait/trait_test.go | 13 +-- pkg/trait/trait_types.go | 44 +++++--- pkg/util/source/inspector.go | 6 ++ resources/traits.yaml | 1 - 21 files changed, 376 insertions(+), 102 deletions(-) diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc index 4b9e182a0..3d19c2158 100644 --- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc +++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc @@ -5406,11 +5406,9 @@ The Quarkus trait configures the Quarkus runtime. It's enabled by default. -NOTE: Compiling to a native executable, i.e. when using `package-type=native`, is only supported -for kamelets, as well as YAML and XML integrations. -It also requires at least 4GiB of memory, so the Pod running the native build, that is either -the operator Pod, or the build Pod (depending on the build strategy configured for the platform), -must have enough memory available. +NOTE: Compiling to a native executable, i.e. when using `package-type=native`, requires at least +4GiB of memory, so the Pod running the native build, that is either the operator Pod, or the build +Pod (depending on the build strategy configured for the platform), must have enough memory available. [cols="2,2a",options="header"] diff --git a/docs/modules/traits/pages/quarkus.adoc b/docs/modules/traits/pages/quarkus.adoc index 595572dd6..e0201cc13 100755 --- a/docs/modules/traits/pages/quarkus.adoc +++ b/docs/modules/traits/pages/quarkus.adoc @@ -5,11 +5,9 @@ The Quarkus trait configures the Quarkus runtime. It's enabled by default. -NOTE: Compiling to a native executable, i.e. when using `package-type=native`, is only supported -for kamelets, as well as YAML and XML integrations. -It also requires at least 4GiB of memory, so the Pod running the native build, that is either -the operator Pod, or the build Pod (depending on the build strategy configured for the platform), -must have enough memory available. +NOTE: Compiling to a native executable, i.e. when using `package-type=native`, requires at least +4GiB of memory, so the Pod running the native build, that is either the operator Pod, or the build +Pod (depending on the build strategy configured for the platform), must have enough memory available. This trait is available in the following profiles: **Kubernetes, Knative, OpenShift**. diff --git a/e2e/namespace/native/files/Java.java b/e2e/namespace/native/files/Groovy.groovy similarity index 74% copy from e2e/namespace/native/files/Java.java copy to e2e/namespace/native/files/Groovy.groovy index 66fef5fe8..baeaa9c0c 100644 --- a/e2e/namespace/native/files/Java.java +++ b/e2e/namespace/native/files/Groovy.groovy @@ -15,14 +15,7 @@ * limitations under the License. */ -import org.apache.camel.builder.RouteBuilder; - -public class Java extends RouteBuilder { - @Override - public void configure() throws Exception { - from("timer:tick") - .setHeader("m").constant("string!") - .setBody().simple("Magic${header.m}") - .log("${body}"); - } -} +from('timer:tick') + .setHeader('m').constant('string!') + .setBody().simple('Magic${header.m}') + .log('Groovy ${body}') diff --git a/e2e/namespace/native/files/Java.java b/e2e/namespace/native/files/Java.java index 66fef5fe8..35ee313e9 100644 --- a/e2e/namespace/native/files/Java.java +++ b/e2e/namespace/native/files/Java.java @@ -20,9 +20,9 @@ import org.apache.camel.builder.RouteBuilder; public class Java extends RouteBuilder { @Override public void configure() throws Exception { - from("timer:tick") - .setHeader("m").constant("string!") - .setBody().simple("Magic${header.m}") - .log("${body}"); + from("timer:tick") + .setHeader("m").constant("string!") + .setBody().simple("Magic${header.m}") + .log("Java ${body}"); } } diff --git a/e2e/namespace/native/files/Java.java b/e2e/namespace/native/files/JavaScript.js similarity index 74% copy from e2e/namespace/native/files/Java.java copy to e2e/namespace/native/files/JavaScript.js index 66fef5fe8..0d3194e58 100644 --- a/e2e/namespace/native/files/Java.java +++ b/e2e/namespace/native/files/JavaScript.js @@ -15,14 +15,7 @@ * limitations under the License. */ -import org.apache.camel.builder.RouteBuilder; - -public class Java extends RouteBuilder { - @Override - public void configure() throws Exception { - from("timer:tick") - .setHeader("m").constant("string!") - .setBody().simple("Magic${header.m}") - .log("${body}"); - } -} +from('timer:tick') + .setHeader('m').constant('string!') + .setBody().simple('Magic${header.m}') + .log('JavaScript ${body}') diff --git a/e2e/namespace/native/files/Java.java b/e2e/namespace/native/files/JavaShell.jsh similarity index 74% copy from e2e/namespace/native/files/Java.java copy to e2e/namespace/native/files/JavaShell.jsh index 66fef5fe8..32f3b4ea0 100644 --- a/e2e/namespace/native/files/Java.java +++ b/e2e/namespace/native/files/JavaShell.jsh @@ -15,14 +15,7 @@ * limitations under the License. */ -import org.apache.camel.builder.RouteBuilder; - -public class Java extends RouteBuilder { - @Override - public void configure() throws Exception { - from("timer:tick") - .setHeader("m").constant("string!") - .setBody().simple("Magic${header.m}") - .log("${body}"); - } -} +builder.from("timer:tick") + .setHeader("m").constant("string!") + .setBody().simple("Magic${header.m}") + .log("JShell ${body}"); diff --git a/e2e/namespace/native/files/Java.java b/e2e/namespace/native/files/Kotlin.kts similarity index 74% copy from e2e/namespace/native/files/Java.java copy to e2e/namespace/native/files/Kotlin.kts index 66fef5fe8..743259b38 100644 --- a/e2e/namespace/native/files/Java.java +++ b/e2e/namespace/native/files/Kotlin.kts @@ -14,15 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import org.apache.camel.builder.RouteBuilder; - -public class Java extends RouteBuilder { - @Override - public void configure() throws Exception { - from("timer:tick") - .setHeader("m").constant("string!") - .setBody().simple("Magic${header.m}") - .log("${body}"); - } -} + +from("timer:tick") + .setHeader("m").constant("string!") + .setBody().simple("Magic\${header.m}") + .log("Kotlin \${body}"); diff --git a/e2e/namespace/native/files/Xml.xml b/e2e/namespace/native/files/Xml.xml new file mode 100644 index 000000000..7b55c2d62 --- /dev/null +++ b/e2e/namespace/native/files/Xml.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://camel.apache.org/schema/spring" + xsi:schemaLocation=" + http://camel.apache.org/schema/spring + https://camel.apache.org/schema/spring/camel-spring.xsd"> + + <route id="hello"> + <from uri="timer:tick"/> + <setHeader name="m"> + <constant>string!</constant> + </setHeader> + <setBody> + <simple>Magic${header.m}</simple> + </setBody> + <log message="XML ${body}"/> + </route> + +</routes> diff --git a/e2e/namespace/native/native_test.go b/e2e/namespace/native/native_test.go index 6ea8855b0..d8da8d9c3 100644 --- a/e2e/namespace/native/native_test.go +++ b/e2e/namespace/native/native_test.go @@ -38,14 +38,14 @@ func TestNativeIntegrations(t *testing.T) { operatorID := "camel-k-quarkus-native" Expect(KamelInstallWithID(operatorID, ns, "--build-timeout", "90m0s", - "--operator-resources", "limits.memory=4.5Gi", - "--maven-cli-option", "-Dquarkus.native.native-image-xmx=3g", + "--operator-resources", "limits.memory=5.5Gi", + "--maven-cli-option", "-Dquarkus.native.native-image-xmx=4g", ).Execute()).To(Succeed()) Eventually(PlatformPhase(ns), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady)) t.Run("unsupported integration source language", func(t *testing.T) { - name := "unsupported-java" - Expect(KamelRunWithID(operatorID, ns, "files/Java.java", "--name", name, + name := "unsupported-js" + Expect(KamelRunWithID(operatorID, ns, "files/JavaScript.js", "--name", name, "-t", "quarkus.package-type=native", ).Execute()).To(Succeed()) @@ -126,6 +126,116 @@ func TestNativeIntegrations(t *testing.T) { Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) }) + t.Run("java native support", func(t *testing.T) { + name := "java-native" + Expect(KamelRunWithID(operatorID, ns, "files/Java.java", "--name", name, + "-t", "quarkus.package-type=native", + ).Execute()).To(Succeed()) + + Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + Eventually(IntegrationPod(ns, name), TestTimeoutShort). + Should(WithTransform(getContainerCommand(), MatchRegexp(".*camel-k-integration-\\d+\\.\\d+\\.\\d+[-A-Za-z]*-runner.*"))) + Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort). + Should(Equal(corev1.ConditionTrue)) + + Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Java Magicstring!")) + + // Clean up + Expect(Kamel("delete", name, "-n", ns).Execute()).To(Succeed()) + }) + + t.Run("xml native support", func(t *testing.T) { + name := "xml-native" + Expect(KamelRunWithID(operatorID, ns, "files/Xml.xml", "--name", name, + "-t", "quarkus.package-type=native", + ).Execute()).To(Succeed()) + + Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + Eventually(IntegrationPod(ns, name), TestTimeoutShort). + Should(WithTransform(getContainerCommand(), MatchRegexp(".*camel-k-integration-\\d+\\.\\d+\\.\\d+[-A-Za-z]*-runner.*"))) + Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort). + Should(Equal(corev1.ConditionTrue)) + + Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("XML Magicstring!")) + + // Clean up + Expect(Kamel("delete", name, "-n", ns).Execute()).To(Succeed()) + }) + + t.Run("groovy native support", func(t *testing.T) { + name := "groovy-native" + Expect(KamelRunWithID(operatorID, ns, "files/Groovy.groovy", "--name", name, + "-t", "quarkus.package-type=native", + ).Execute()).To(Succeed()) + + Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + Eventually(IntegrationPod(ns, name), TestTimeoutShort). + Should(WithTransform(getContainerCommand(), MatchRegexp(".*camel-k-integration-\\d+\\.\\d+\\.\\d+[-A-Za-z]*-runner.*"))) + Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort). + Should(Equal(corev1.ConditionTrue)) + + Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Groovy Magicstring!")) + + // Clean up + Expect(Kamel("delete", name, "-n", ns).Execute()).To(Succeed()) + }) + + // Native support is not yet supported due to https://github.com/apache/camel-quarkus/issues/4458 + t.Run("jshell native support", func(t *testing.T) { + name := "jshell-native" + Expect(KamelRunWithID(operatorID, ns, "files/JavaShell.jsh", "--name", name). + // "-t", "quarkus.package-type=native", + Execute()).To(Succeed()) + + Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + // Eventually(IntegrationPod(ns, name), TestTimeoutShort). + // Should(WithTransform(getContainerCommand(), MatchRegexp(".*camel-k-integration-\\d+\\.\\d+\\.\\d+[-A-Za-z]*-runner.*"))) + Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort). + Should(Equal(corev1.ConditionTrue)) + + Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("JShell Magicstring!")) + + // Clean up + Expect(Kamel("delete", name, "-n", ns).Execute()).To(Succeed()) + }) + + // Guest languages are not yet supported on Mandrel in native mode + t.Run("js native support", func(t *testing.T) { + name := "js-native" + Expect(KamelRunWithID(operatorID, ns, "files/JavaScript.js", "--name", name). + // "-t", "quarkus.package-type=native", + Execute()).To(Succeed()) + + Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + // Eventually(IntegrationPod(ns, name), TestTimeoutShort). + // Should(WithTransform(getContainerCommand(), MatchRegexp(".*camel-k-integration-\\d+\\.\\d+\\.\\d+[-A-Za-z]*-runner.*"))) + Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort). + Should(Equal(corev1.ConditionTrue)) + + Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("JavaScript Magicstring!")) + + // Clean up + Expect(Kamel("delete", name, "-n", ns).Execute()).To(Succeed()) + }) + + t.Run("kotlin native support", func(t *testing.T) { + name := "kotlin-native" + Expect(KamelRunWithID(operatorID, ns, "files/Kotlin.kts", "--name", name, + "-t", "quarkus.package-type=native", + ).Execute()).To(Succeed()) + + Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + Eventually(IntegrationPod(ns, name), TestTimeoutShort). + Should(WithTransform(getContainerCommand(), MatchRegexp(".*camel-k-integration-\\d+\\.\\d+\\.\\d+[-A-Za-z]*-runner.*"))) + Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort). + Should(Equal(corev1.ConditionTrue)) + + Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Kotlin Magicstring!")) + + // Clean up + Expect(Kamel("delete", name, "-n", ns).Execute()).To(Succeed()) + }) + // Clean up Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed()) }) diff --git a/pkg/apis/camel/v1/common_types.go b/pkg/apis/camel/v1/common_types.go index 33eec9f89..48ebb584a 100644 --- a/pkg/apis/camel/v1/common_types.go +++ b/pkg/apis/camel/v1/common_types.go @@ -424,6 +424,8 @@ const ( LanguageYaml Language = "yaml" // LanguageKamelet used for Kamelets LanguageKamelet Language = "kamelet" + // LanguageJavaShell used for Java Shell + LanguageJavaShell Language = "jsh" ) // Languages is the list of all supported languages @@ -435,4 +437,5 @@ var Languages = []Language{ LanguageKotlin, LanguageYaml, LanguageKamelet, + LanguageJavaShell, } diff --git a/pkg/apis/camel/v1/trait/quarkus.go b/pkg/apis/camel/v1/trait/quarkus.go index dcbd079d6..571ad6206 100644 --- a/pkg/apis/camel/v1/trait/quarkus.go +++ b/pkg/apis/camel/v1/trait/quarkus.go @@ -21,11 +21,9 @@ package trait // // It's enabled by default. // -// NOTE: Compiling to a native executable, i.e. when using `package-type=native`, is only supported -// for kamelets, as well as YAML and XML integrations. -// It also requires at least 4GiB of memory, so the Pod running the native build, that is either -// the operator Pod, or the build Pod (depending on the build strategy configured for the platform), -// must have enough memory available. +// NOTE: Compiling to a native executable, i.e. when using `package-type=native`, requires at least +// 4GiB of memory, so the Pod running the native build, that is either the operator Pod, or the build +// Pod (depending on the build strategy configured for the platform), must have enough memory available. // // +camel-k:trait=quarkus. type QuarkusTrait struct { diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go index f97274ebb..ffc4dc05d 100644 --- a/pkg/builder/builder.go +++ b/pkg/builder/builder.go @@ -27,6 +27,7 @@ import ( v1 "github.com/apache/camel-k/pkg/apis/camel/v1" "github.com/apache/camel-k/pkg/client" + "github.com/apache/camel-k/pkg/util/kubernetes" "github.com/apache/camel-k/pkg/util/log" ) @@ -54,14 +55,20 @@ func (t *builderTask) Do(ctx context.Context) v1.BuildStatus { } buildDir = pwd } + buildCreator := kubernetes.GetCamelCreator(t.build) + var integration string + if buildCreator != nil { + integration = buildCreator.Name + } c := builderContext{ - Client: t.c, - C: ctx, - Path: buildDir, - Namespace: t.build.Namespace, - Build: *t.task, - BaseImage: t.task.BaseImage, + Client: t.c, + C: ctx, + Path: buildDir, + Namespace: t.build.Namespace, + Integration: integration, + Build: *t.task, + BaseImage: t.task.BaseImage, } steps, err := StepsFrom(t.task.Steps...) diff --git a/pkg/builder/quarkus.go b/pkg/builder/quarkus.go index 91d6bfa33..9f7ba0ec2 100644 --- a/pkg/builder/quarkus.go +++ b/pkg/builder/quarkus.go @@ -27,10 +27,13 @@ import ( "github.com/pkg/errors" v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + "github.com/apache/camel-k/pkg/client" "github.com/apache/camel-k/pkg/util/camel" "github.com/apache/camel-k/pkg/util/defaults" "github.com/apache/camel-k/pkg/util/digest" + "github.com/apache/camel-k/pkg/util/kubernetes" "github.com/apache/camel-k/pkg/util/maven" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) func init() { @@ -48,6 +51,7 @@ type quarkusSteps struct { GenerateQuarkusProject Step BuildQuarkusRunner Step ComputeQuarkusDependencies Step + PrepareProjectWithSources Step CommonSteps []Step } @@ -55,10 +59,67 @@ type quarkusSteps struct { var Quarkus = quarkusSteps{ LoadCamelQuarkusCatalog: NewStep(InitPhase, loadCamelQuarkusCatalog), GenerateQuarkusProject: NewStep(ProjectGenerationPhase, generateQuarkusProject), + PrepareProjectWithSources: NewStep(ProjectBuildPhase-1, prepareProjectWithSources), BuildQuarkusRunner: NewStep(ProjectBuildPhase, buildQuarkusRunner), ComputeQuarkusDependencies: NewStep(ProjectBuildPhase+1, computeQuarkusDependencies), } +func getIntegration(ctx context.Context, c client.Client, name string, namespace string) (*v1.Integration, error) { + key := k8sclient.ObjectKey{ + Name: name, + Namespace: namespace, + } + answer := v1.NewIntegration(namespace, name) + if err := c.Get(ctx, key, &answer); err != nil { + return nil, err + } + return &answer, nil +} + +func resolveIntegrationSources(ctx *builderContext) ([]v1.SourceSpec, error) { + if ctx.Integration == "" { + return nil, errors.New("No integration name provided") + } + integration, err := getIntegration(ctx.C, ctx.Client, ctx.Integration, ctx.Namespace) + if err != nil { + return nil, err + } + return kubernetes.ResolveIntegrationSources(ctx.C, ctx.Client, integration, &kubernetes.Collection{}) +} + +func prepareProjectWithSources(ctx *builderContext) error { + sources, err := resolveIntegrationSources(ctx) + if err != nil { + return err + } + if sources == nil || len(sources) == 0 { + return nil + } + sourcesPath := filepath.Join(ctx.Path, "maven", "src", "main", "resources", "routes") + if err := os.MkdirAll(sourcesPath, os.ModePerm); err != nil { + return errors.Wrap(err, "failure while creating resource folder") + } + + sourceList := "" + for _, source := range sources { + if sourceList != "" { + sourceList += "," + } + sourceList += "classpath:routes/" + source.Name + if err := os.WriteFile(filepath.Join(sourcesPath, source.Name), []byte(source.Content), os.ModePerm); err != nil { + return errors.Wrapf(err, "failure while writing %s", source.Name) + } + } + + if sourceList != "" { + routesIncludedPattern := "camel.main.routes-include-pattern = " + sourceList + if err := os.WriteFile(filepath.Join(filepath.Dir(sourcesPath), "application.properties"), []byte(routesIncludedPattern), os.ModePerm); err != nil { + return errors.Wrapf(err, "failure while writing the configuration application.properties") + } + } + return nil +} + func loadCamelQuarkusCatalog(ctx *builderContext) error { catalog, err := camel.LoadCatalog(ctx.C, ctx.Client, ctx.Namespace, ctx.Build.Runtime) if err != nil { @@ -196,7 +257,7 @@ func BuildQuarkusRunnerCommon(ctx context.Context, mc maven.Context, project mav // may fail the build. // In the future there should be a way to provide build information from secrets, // configmap, etc. - if _, err := os.Create(filepath.Join(resourcesPath, "application.properties")); err != nil { + if _, err := os.OpenFile(filepath.Join(resourcesPath, "application.properties"), os.O_RDWR|os.O_CREATE, 0666); err != nil { return errors.Wrap(err, "failure while creating application.properties") } diff --git a/pkg/builder/types.go b/pkg/builder/types.go index bbd684d6b..14898ad42 100644 --- a/pkg/builder/types.go +++ b/pkg/builder/types.go @@ -80,6 +80,7 @@ type builderContext struct { Build v1.BuilderTask BaseImage string Namespace string + Integration string Path string Artifacts []v1.Artifact SelectedArtifacts []v1.Artifact diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go index 139e4ee49..4d28c4987 100644 --- a/pkg/resources/resources.go +++ b/pkg/resources/resources.go @@ -604,16 +604,16 @@ var assets = func() http.FileSystem { "/camel-catalog-1.17.0-SNAPSHOT.yaml": &vfsgen۰CompressedFileInfo{ name: "camel-catalog-1.17.0-SNAPSHOT.yaml", modTime: time.Time{}, - uncompressedSize: 86658, + uncompressedSize: 86428, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\xbd\x4b\x77\xdb\xb8\xb2\x36\x3c\xcf\xaf\xe0\xea\x4c\xce\x59\xdf\x16\xba\xdb\x7d\xbe\xdd\xef\xca\x3b\xb2\xe5\x38\xb1\x63\x3b\x4e\xe4\x9d\xce\xde\x93\x5e\x10\x09\x49\xb0\x48\x82\x06\x40\x59\xce\xaf\x7f\x17\x40\xf0\x2a\xa5\x78\x71\xc1\xc7\x03\x93\x22\x0a\x4f\xa1\x1e\x80\xb8\xb3\xf0\x36\x98\xe1\xfd\xbd\x79\x1b\x5c\xf3\x90\xa5\x8a\x45\x81\x16\x81\xde\xb0\xe0\x34\xa3\xe1\x86\x05\x0b\xb1\xd2\x4f\x54\xb2\xe0\x42\xe4\x69\x [...] + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\xbd\x4b\x77\xdb\xb8\xb2\x36\x3c\xcf\xaf\xe0\xea\x4c\xce\x59\xdf\x16\xba\xdb\x7d\xbe\xdd\xef\xca\x3b\xb2\xe5\x38\xb1\x63\x3b\x4e\xe4\x9d\xce\xde\x93\x5e\x10\x09\x49\xb0\x48\x82\x06\x40\x59\xce\xaf\x7f\x17\x40\xf0\x2a\xa5\x78\x71\xc1\xc7\x03\x93\x22\x0a\x4f\xa1\x1e\x80\xb8\xb3\xf0\x36\x98\xe1\xfd\xbd\x79\x1b\x5c\xf3\x90\xa5\x8a\x45\x81\x16\x81\xde\xb0\xe0\x34\xa3\xe1\x86\x05\x0b\xb1\xd2\x4f\x54\xb2\xe0\x42\xe4\x69\x [...] }, "/traits.yaml": &vfsgen۰CompressedFileInfo{ name: "traits.yaml", modTime: time.Time{}, - uncompressedSize: 58334, + uncompressedSize: 58252, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\xfd\x73\x1c\x37\x92\x20\xfa\xbb\xfe\x0a\x04\xf7\x6d\x90\xd4\xeb\x6e\xca\x9e\xf5\xac\x97\xef\x69\xe7\x68\x59\x9e\xa1\xad\x0f\x9e\x48\x7b\x76\x42\xa7\x98\x46\x57\xa1\xbb\xa1\xae\x02\x6a\x00\x14\xc9\xf6\xed\xfd\xef\x17\xc8\x4c\x7c\x54\x75\x35\xbb\x29\x89\xbe\xe1\xcd\xee\x44\xac\x45\xb2\x90\x48\x24\x12\x89\x44\x7e\x3a\xc3\xa5\xb3\xa7\x4f\xc6\x4c\xf1\x5a\x9c\xb2\xdf\xd9\x82\x57\xe2\x09\x63\x4d\xc5\xdd\x5c\x9b\xfa\x [...] + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\x7b\x73\x1c\x37\x92\x20\xfe\xbf\x3e\x05\x82\xfb\xdb\x20\xa9\x5f\x77\x53\xf6\xac\x67\xbd\xbc\xd3\xce\xd1\xb2\x3c\x43\x5b\x0f\x9e\x48\x7b\x6e\x42\xa7\x98\x46\x57\xa1\xbb\xa1\xae\x02\x6a\x00\x14\xc9\xf6\xed\x7d\xf7\x0b\x64\x26\x1e\x55\x5d\xcd\x6e\x4a\xa2\x6f\x78\xb3\x3b\x11\x6b\x91\x2c\x24\x12\x89\x44\x22\x91\x4f\x67\xb8\x74\xf6\xf4\xc9\x98\x29\x5e\x8b\x53\xf6\x3b\x5b\xf0\x4a\x3c\x61\xac\xa9\xb8\x9b\x6b\x53\x9f\x [...] }, } fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ diff --git a/pkg/trait/camel.go b/pkg/trait/camel.go index 26d87c589..50348255c 100644 --- a/pkg/trait/camel.go +++ b/pkg/trait/camel.go @@ -217,7 +217,7 @@ func (t *camelTrait) computeConfigMaps(e *Environment) []ctrl.Object { } for i, s := range sources { - if s.ContentRef != "" { + if s.ContentRef != "" || e.isEmbedded(s) { continue } diff --git a/pkg/trait/quarkus.go b/pkg/trait/quarkus.go index ff3decb31..4042074e5 100644 --- a/pkg/trait/quarkus.go +++ b/pkg/trait/quarkus.go @@ -47,6 +47,57 @@ type quarkusTrait struct { BaseTrait traitv1.QuarkusTrait `property:",squash"` } +type languageSettings struct { + // indicates whether the native mode is supported + Native bool + // indicates whether the sources are required at build time for native compilation + SourcesRequiredAtBuildTime bool +} + +var ( + // settings for the Java language + javaSettings = languageSettings{true, true} + // settings for the Groovy language + groovySettings = languageSettings{true, true} + // settings for the XML language + xmlSettings = languageSettings{true, false} + // settings for the Kotlin language + kotlinSettings = languageSettings{true, true} + // settings for the YAML language + yamlSettings = languageSettings{true, false} + // settings for the Kamelet language + kameletSettings = languageSettings{true, false} + // settings for the Java Shell language + // Native support is not yet supported due to https://github.com/apache/camel-quarkus/issues/4458 + jshSettings = languageSettings{false /*true*/, true} + // settings for the JavaScript language + // Guest languages are not yet supported on Mandrel in native mode + javaScriptSettings = languageSettings{false /*true*/, false} + // settings for an unknown language + defaultSettings = languageSettings{false, false} +) + +func getLanguageSettings(language v1.Language) languageSettings { + switch language { + case v1.LanguageJavaSource: + return javaSettings + case v1.LanguageGroovy: + return groovySettings + case v1.LanguageXML: + return xmlSettings + case v1.LanguageKotlin: + return kotlinSettings + case v1.LanguageYaml: + return yamlSettings + case v1.LanguageKamelet: + return kameletSettings + case v1.LanguageJavaShell: + return jshSettings + case v1.LanguageJavaScript: + return javaScriptSettings + } + return defaultSettings +} func newQuarkusTrait() Trait { return &quarkusTrait{ @@ -161,9 +212,7 @@ func (t *quarkusTrait) applyWhileBuildingKit(e *Environment) { func (t *quarkusTrait) validateNativeSupport(e *Environment) bool { for _, source := range e.Integration.Sources() { - if language := source.InferLanguage(); language != v1.LanguageKamelet && - language != v1.LanguageYaml && - language != v1.LanguageXML { + if language := source.InferLanguage(); !getLanguageSettings(language).Native { t.L.ForIntegration(e.Integration).Infof("Integration %s/%s contains a %s source that cannot be compiled to native executable", e.Integration.Namespace, e.Integration.Name, language) e.Integration.Status.Phase = v1.IntegrationPhaseError e.Integration.Status.SetCondition( @@ -265,6 +314,7 @@ func (t *quarkusTrait) applyWhenBuildSubmitted(e *Environment) error { if native { build.Maven.Properties["quarkus.package.type"] = string(traitv1.NativePackageType) + steps = append(steps, builder.Quarkus.PrepareProjectWithSources) steps = append(steps, builder.Image.NativeImageContext) // Spectrum does not rely on Dockerfile to assemble the image if e.Platform.Status.Build.PublishStrategy != v1.IntegrationPlatformBuildPublishStrategySpectrum { @@ -319,6 +369,18 @@ func (t *quarkusTrait) isNativeIntegration(e *Environment) bool { return e.IntegrationKit.Labels[v1.IntegrationKitLayoutLabel] == v1.IntegrationKitLayoutNative } +func (t *quarkusTrait) isEmbedded(e *Environment, source v1.SourceSpec) bool { + if language := source.InferLanguage(); IsSourcesRequiredAtBuildTime(language) { + if e.IntegrationInRunningPhases() { + return t.isNativeIntegration(e) + } else if e.IntegrationKitInPhase(v1.IntegrationKitPhaseBuildSubmitted) { + native, _ := t.isNativeKit(e) + return native + } + } + return false +} + func containsPackageType(types []traitv1.QuarkusPackageType, t traitv1.QuarkusPackageType) bool { for _, ti := range types { if t == ti { @@ -327,3 +389,8 @@ func containsPackageType(types []traitv1.QuarkusPackageType, t traitv1.QuarkusPa } return false } + +// Indicates whether the sources expressed in the given languages are required at build time +func IsSourcesRequiredAtBuildTime(language v1.Language) bool { + return getLanguageSettings(language).SourcesRequiredAtBuildTime +} diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go index 63fcb07d0..dbc23a36e 100644 --- a/pkg/trait/trait_test.go +++ b/pkg/trait/trait_test.go @@ -267,15 +267,15 @@ func TestConfigureVolumesAndMountsSources(t *testing.T) { Sources: []v1.SourceSpec{ { DataSpec: v1.DataSpec{ - Name: "source1.java", + Name: "source1.xml", ContentRef: "my-cm1", - ContentKey: "source1.java", + ContentKey: "source1.xml", }, Type: "data", }, { DataSpec: v1.DataSpec{ - Name: "source2.java", + Name: "source2.xml", ContentRef: "my-cm2", }, Type: "data", @@ -283,6 +283,7 @@ func TestConfigureVolumesAndMountsSources(t *testing.T) { }, }, }, + Catalog: &Catalog{}, } vols := make([]corev1.Volume, 0) @@ -297,7 +298,7 @@ func TestConfigureVolumesAndMountsSources(t *testing.T) { assert.NotNil(t, v) assert.NotNil(t, v.VolumeSource.ConfigMap) assert.Len(t, v.VolumeSource.ConfigMap.Items, 1) - assert.Equal(t, "source1.java", v.VolumeSource.ConfigMap.Items[0].Key) + assert.Equal(t, "source1.xml", v.VolumeSource.ConfigMap.Items[0].Key) m := findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name }) assert.NotNil(t, m) @@ -403,10 +404,10 @@ func createTestEnv(t *testing.T, cluster v1.IntegrationPlatformCluster, script s Sources: []v1.SourceSpec{ { DataSpec: v1.DataSpec{ - Name: "file.groovy", + Name: "file.js", Content: script, }, - Language: v1.LanguageGroovy, + Language: v1.LanguageJavaScript, }, }, }, diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go index b38b9a7d8..ab03f8f7e 100644 --- a/pkg/trait/trait_types.go +++ b/pkg/trait/trait_types.go @@ -405,32 +405,36 @@ func (e *Environment) addSourcesProperties() { if e.ApplicationProperties == nil { e.ApplicationProperties = make(map[string]string) } - for i, s := range e.Integration.Sources() { + idx := 0 + for _, s := range e.Integration.Sources() { + if e.isEmbedded(s) { + continue + } srcName := strings.TrimPrefix(filepath.ToSlash(s.Name), "/") src := "file:" + path.Join(filepath.ToSlash(camel.SourcesMountPath), srcName) - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].location", i)] = src + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].location", idx)] = src simpleName := srcName if strings.Contains(srcName, ".") { simpleName = srcName[0:strings.Index(srcName, ".")] } - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].name", i)] = simpleName + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].name", idx)] = simpleName for pid, p := range s.PropertyNames { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].property-names[%d]", i, pid)] = p + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].property-names[%d]", idx, pid)] = p } if s.Type != "" { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].type", i)] = string(s.Type) + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].type", idx)] = string(s.Type) } if s.InferLanguage() != "" { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].language", i)] = string(s.InferLanguage()) + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].language", idx)] = string(s.InferLanguage()) } if s.Loader != "" { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].loader", i)] = s.Loader + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].loader", idx)] = s.Loader } if s.Compression { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].compressed", i)] = "true" + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].compressed", idx)] = "true" } interceptors := make([]string, 0, len(s.Interceptors)) @@ -441,8 +445,9 @@ func (e *Environment) addSourcesProperties() { interceptors = append(interceptors, e.Interceptors...) } for intID, interceptor := range interceptors { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].interceptors[%d]", i, intID)] = interceptor + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].interceptors[%d]", idx, intID)] = interceptor } + idx++ } } @@ -450,8 +455,12 @@ func (e *Environment) configureVolumesAndMounts(vols *[]corev1.Volume, mnts *[]c // // Volumes :: Sources // - for i, s := range e.Integration.Sources() { - cmName := fmt.Sprintf("%s-source-%03d", e.Integration.Name, i) + idx := 0 + for _, s := range e.Integration.Sources() { + if e.isEmbedded(s) { + continue + } + cmName := fmt.Sprintf("%s-source-%03d", e.Integration.Name, idx) if s.ContentRef != "" { cmName = s.ContentRef } @@ -460,13 +469,14 @@ func (e *Environment) configureVolumesAndMounts(vols *[]corev1.Volume, mnts *[]c cmKey = s.ContentKey } resName := strings.TrimPrefix(s.Name, "/") - refName := fmt.Sprintf("i-source-%03d", i) + refName := fmt.Sprintf("i-source-%03d", idx) resPath := filepath.Join(camel.SourcesMountPath, resName) vol := getVolume(refName, "configmap", cmName, cmKey, resName) mnt := getMount(refName, resPath, resName, true) *vols = append(*vols, *vol) *mnts = append(*mnts, *mnt) + idx++ } if e.Resources != nil { @@ -651,6 +661,16 @@ func (e *Environment) GetIntegrationContainerName() string { return containerName } +// Indicates whether the given source is embedded in the final binary +func (e *Environment) isEmbedded(source v1.SourceSpec) bool { + if dt := e.Catalog.GetTrait(quarkusTraitID); dt != nil { + if qt, ok := dt.(*quarkusTrait); ok { + return qt.isEmbedded(e, source) + } + } + return false +} + func (e *Environment) GetIntegrationContainer() *corev1.Container { containerName := e.GetIntegrationContainerName() return e.Resources.GetContainerByName(containerName) diff --git a/pkg/util/source/inspector.go b/pkg/util/source/inspector.go index 33fa7b2b7..7be1eaf54 100644 --- a/pkg/util/source/inspector.go +++ b/pkg/util/source/inspector.go @@ -207,6 +207,12 @@ func InspectorForLanguage(catalog *camel.RuntimeCatalog, language v1.Language) I catalog: catalog, }, } + case v1.LanguageJavaShell: + return &JavaSourceInspector{ + baseInspector: baseInspector{ + catalog: catalog, + }, + } } return &baseInspector{} } diff --git a/resources/traits.yaml b/resources/traits.yaml index a5fef3a9f..7059df2b7 100755 --- a/resources/traits.yaml +++ b/resources/traits.yaml @@ -1100,7 +1100,6 @@ traits: - OpenShift description: 'The Quarkus trait configures the Quarkus runtime. It''s enabled by default. NOTE: Compiling to a native executable, i.e. when using `package-type=native`, - is only supported for kamelets, as well as YAML and XML integrations. It also requires at least 4GiB of memory, so the Pod running the native build, that is either the operator Pod, or the build Pod (depending on the build strategy configured for the platform), must have enough memory available.'
