This is an automated email from the ASF dual-hosted git repository. astefanutti pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push: new 1215301 chore(native): Fail-fast native build for unsupported languages 1215301 is described below commit 1215301982c3fd531c5d8f611eae88ab48e258f0 Author: Antonin Stefanutti <anto...@stefanutti.fr> AuthorDate: Mon Sep 13 12:40:53 2021 +0200 chore(native): Fail-fast native build for unsupported languages --- deploy/traits.yaml | 7 +-- docs/modules/traits/pages/quarkus.adoc | 8 +-- e2e/native/Java.java | 28 ++++++++++ e2e/native/native_test.go | 96 ++++++++++++++++++--------------- pkg/apis/camel/v1/integration_types.go | 2 + pkg/controller/integration/build_kit.go | 13 ++--- pkg/resources/resources.go | 4 +- pkg/trait/quarkus.go | 53 +++++++++++++----- 8 files changed, 141 insertions(+), 70 deletions(-) diff --git a/deploy/traits.yaml b/deploy/traits.yaml index b441c4e..855ba4a 100755 --- a/deploy/traits.yaml +++ b/deploy/traits.yaml @@ -810,9 +810,10 @@ 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`, - requires at least 4GiB of memory. Make sure enough memory is available for 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.' + 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.' properties: - name: enabled type: bool diff --git a/docs/modules/traits/pages/quarkus.adoc b/docs/modules/traits/pages/quarkus.adoc index d90ad66..78e9f72 100755 --- a/docs/modules/traits/pages/quarkus.adoc +++ b/docs/modules/traits/pages/quarkus.adoc @@ -5,9 +5,11 @@ 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`, requires at least 4GiB of memory. -Make sure enough memory is available for 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. +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. This trait is available in the following profiles: **Kubernetes, Knative, OpenShift**. diff --git a/e2e/native/Java.java b/e2e/native/Java.java new file mode 100644 index 0000000..66fef5f --- /dev/null +++ b/e2e/native/Java.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +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}"); + } +} diff --git a/e2e/native/native_test.go b/e2e/native/native_test.go index a1ea20b..fca7c7e 100644 --- a/e2e/native/native_test.go +++ b/e2e/native/native_test.go @@ -37,7 +37,7 @@ var ( withNativeLayout = KitWithLabels(map[string]string{v1.IntegrationKitLayoutLabel: v1.IntegrationKitLayoutNative}) ) -func TestAutomaticRolloutDeploymentFromFastJarToNativeKit(t *testing.T) { +func TestNativeIntegrations(t *testing.T) { WithNewTestNamespace(t, func(ns string) { Expect(Kamel("install", "-n", ns, "--build-timeout", "15m0s", @@ -45,47 +45,59 @@ func TestAutomaticRolloutDeploymentFromFastJarToNativeKit(t *testing.T) { ).Execute()).To(Succeed()) Eventually(PlatformPhase(ns), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady)) - name := "jvm-to-native" - Expect(Kamel("run", "-n", ns, "yaml.yaml", "--name", name, - "-t", "quarkus.package-type=fast-jar", - "-t", "quarkus.package-type=native", - ).Execute()).To(Succeed()) - - // Check that two Kits are created with distinct layout - Eventually(Kits(ns, withFastJarLayout)).Should(HaveLen(1)) - Eventually(Kits(ns, withNativeLayout)).Should(HaveLen(1)) - - // Check the fast-jar Kit is ready - Eventually(Kits(ns, withFastJarLayout, KitWithPhase(v1.IntegrationKitPhaseReady)), - TestTimeoutMedium).Should(HaveLen(1)) - - fastJarKit := Kits(ns, withFastJarLayout, KitWithPhase(v1.IntegrationKitPhaseReady))()[0] - // Check the Integration uses the fast-jar Kit - Eventually(IntegrationKit(ns, name), TestTimeoutShort).Should(Equal(fastJarKit.Name)) - // Check the Integration Pod uses the fast-jar Kit - Eventually(IntegrationPodImage(ns, name)).Should(Equal(fastJarKit.Status.Image)) - - // Check the Integration is ready - Eventually(IntegrationPodPhase(ns, name), TestTimeoutMedium).Should(Equal(corev1.PodRunning)) - Eventually(IntegrationCondition(ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) - - Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) - - // Check the native Kit is ready - Eventually(Kits(ns, withNativeLayout, KitWithPhase(v1.IntegrationKitPhaseReady)), - TestTimeoutLong).Should(HaveLen(1)) - - nativeKit := Kits(ns, withNativeLayout, KitWithPhase(v1.IntegrationKitPhaseReady))()[0] - // Check the Integration uses the native Kit - Eventually(IntegrationKit(ns, name), TestTimeoutShort).Should(Equal(nativeKit.Name)) - // Check the Integration Pod uses the native Kit - Eventually(IntegrationPodImage(ns, name)).Should(Equal(nativeKit.Status.Image)) - - // Check the Integration is still ready - Eventually(IntegrationPodPhase(ns, name), TestTimeoutMedium).Should(Equal(corev1.PodRunning)) - Eventually(IntegrationCondition(ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) - - Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) + t.Run("unsupported integration source language", func(t *testing.T) { + name := "unsupported-java" + Expect(Kamel("run", "-n", ns, "Java.java", "--name", name, + "-t", "quarkus.package-type=native", + ).Execute()).To(Succeed()) + + Eventually(IntegrationPhase(ns, name)).Should(Equal(v1.IntegrationPhaseError)) + Eventually(IntegrationCondition(ns, name, v1.IntegrationConditionKitAvailable)).Should(Equal(corev1.ConditionFalse)) + }) + + t.Run("automatic rollout deployment from fast-jar to native kit", func(t *testing.T) { + name := "jvm-to-native" + Expect(Kamel("run", "-n", ns, "yaml.yaml", "--name", name, + "-t", "quarkus.package-type=fast-jar", + "-t", "quarkus.package-type=native", + ).Execute()).To(Succeed()) + + // Check that two Kits are created with distinct layout + Eventually(Kits(ns, withFastJarLayout)).Should(HaveLen(1)) + Eventually(Kits(ns, withNativeLayout)).Should(HaveLen(1)) + + // Check the fast-jar Kit is ready + Eventually(Kits(ns, withFastJarLayout, KitWithPhase(v1.IntegrationKitPhaseReady)), + TestTimeoutMedium).Should(HaveLen(1)) + + fastJarKit := Kits(ns, withFastJarLayout, KitWithPhase(v1.IntegrationKitPhaseReady))()[0] + // Check the Integration uses the fast-jar Kit + Eventually(IntegrationKit(ns, name), TestTimeoutShort).Should(Equal(fastJarKit.Name)) + // Check the Integration Pod uses the fast-jar Kit + Eventually(IntegrationPodImage(ns, name)).Should(Equal(fastJarKit.Status.Image)) + + // Check the Integration is ready + Eventually(IntegrationPodPhase(ns, name), TestTimeoutMedium).Should(Equal(corev1.PodRunning)) + Eventually(IntegrationCondition(ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) + + Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) + + // Check the native Kit is ready + Eventually(Kits(ns, withNativeLayout, KitWithPhase(v1.IntegrationKitPhaseReady)), + TestTimeoutLong).Should(HaveLen(1)) + + nativeKit := Kits(ns, withNativeLayout, KitWithPhase(v1.IntegrationKitPhaseReady))()[0] + // Check the Integration uses the native Kit + Eventually(IntegrationKit(ns, name), TestTimeoutShort).Should(Equal(nativeKit.Name)) + // Check the Integration Pod uses the native Kit + Eventually(IntegrationPodImage(ns, name)).Should(Equal(nativeKit.Status.Image)) + + // Check the Integration is still ready + Eventually(IntegrationPodPhase(ns, name), TestTimeoutMedium).Should(Equal(corev1.PodRunning)) + Eventually(IntegrationCondition(ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) + + Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) + }) // Clean up Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed()) diff --git a/pkg/apis/camel/v1/integration_types.go b/pkg/apis/camel/v1/integration_types.go index 32125c8..fe4c469 100644 --- a/pkg/apis/camel/v1/integration_types.go +++ b/pkg/apis/camel/v1/integration_types.go @@ -195,6 +195,8 @@ const ( IntegrationConditionReplicaSetReadyReason string = "ReplicaSetReady" // IntegrationConditionReplicaSetNotReadyReason -- IntegrationConditionReplicaSetNotReadyReason string = "ReplicaSetNotReady" + // IntegrationConditionUnsupportedLanguageReason -- + IntegrationConditionUnsupportedLanguageReason string = "UnsupportedLanguage" // IntegrationConditionKameletsAvailable -- IntegrationConditionKameletsAvailable IntegrationConditionType = "KameletsAvailable" diff --git a/pkg/controller/integration/build_kit.go b/pkg/controller/integration/build_kit.go index aab2ba2..17e3c85 100644 --- a/pkg/controller/integration/build_kit.go +++ b/pkg/controller/integration/build_kit.go @@ -116,12 +116,13 @@ kits: } } - // Set the kit name so the next handle loop, will fall through the - // same path as integration with a user defined kit - integration.SetIntegrationKit(integrationKit) - - if integrationKit.Status.Phase == v1.IntegrationKitPhaseReady { - integration.Status.Phase = v1.IntegrationPhaseDeploying + if integrationKit != nil { + // Set the kit name so the next handle loop, will fall through the + // same path as integration with a user defined kit + integration.SetIntegrationKit(integrationKit) + if integrationKit.Status.Phase == v1.IntegrationKitPhaseReady { + integration.Status.Phase = v1.IntegrationPhaseDeploying + } } return integration, nil diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go index d38bfdf..d053a94 100644 --- a/pkg/resources/resources.go +++ b/pkg/resources/resources.go @@ -467,9 +467,9 @@ var assets = func() http.FileSystem { "/traits.yaml": &vfsgen۰CompressedFileInfo{ name: "traits.yaml", modTime: time.Time{}, - uncompressedSize: 42503, + uncompressedSize: 42583, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\x7d\x73\x1c\xb7\xd1\x20\xfe\xbf\x3e\x05\x8a\xcf\xaf\x4a\x24\x6b\x77\x28\x27\x4f\x12\xff\x78\xe7\x4b\xd1\x92\x1c\xd3\xd6\x0b\x4f\xa2\x9d\xba\xd2\xb9\xb2\xd8\x99\xde\x5d\x68\x31\xc0\x04\xc0\x90\xda\xdc\x73\xdf\xfd\x0a\xdd\x78\x9b\xd9\x25\xb9\x94\x45\x9f\x79\xf5\x24\x7f\x58\x24\x07\x40\xa3\xd1\xe8\xf7\x6e\x38\xc3\x85\xb3\xa7\x4f\xa6\x4c\xf1\x16\x4e\x19\x5f\x2c\x84\x12\x6e\xf3\x84\xb1\x4e\x72\xb7\xd0\xa6\x3d\x65\x [...] + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x6b\x73\x5b\xb9\xb1\xe0\x77\xff\x0a\x94\xee\x56\x59\x52\x91\x94\x27\xb9\x49\x66\xb5\x3b\x9b\xd2\x78\x3c\x89\x66\xfc\xd0\xda\xce\x64\x6f\x79\xa7\x42\xf0\x9c\x26\x09\x0b\x04\x4e\x00\x1c\xc9\xcc\xde\xfd\xef\x5b\xe8\x6e\x3c\x0e\x49\x49\x94\xc7\x9a\x8d\x6e\xdd\xe4\xc3\x58\xd2\x01\xd0\x68\x34\xfa\xdd\x8d\xe0\xa4\x0a\xfe\xf4\xc9\x58\x18\xb9\x82\x53\x21\xe7\x73\x65\x54\x58\x3f\x11\xa2\xd3\x32\xcc\xad\x5b\x9d\x8a\xb9\x [...] }, } fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ diff --git a/pkg/trait/quarkus.go b/pkg/trait/quarkus.go index c4dd613..0b76021 100644 --- a/pkg/trait/quarkus.go +++ b/pkg/trait/quarkus.go @@ -24,6 +24,8 @@ import ( "github.com/rs/xid" + corev1 "k8s.io/api/core/v1" + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" "github.com/apache/camel-k/pkg/builder" "github.com/apache/camel-k/pkg/util/defaults" @@ -48,9 +50,11 @@ var kitPriority = map[quarkusPackageType]string{ // // It's enabled by default. // -// NOTE: Compiling to a native executable, i.e. when using `package-type=native`, requires at least 4GiB of memory. -// Make sure enough memory is available for 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. +// 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. // // +camel-k:trait=quarkus type quarkusTrait struct { @@ -92,16 +96,7 @@ func (t *quarkusTrait) Matches(trait Trait) bool { return false } - contains := func(types []quarkusPackageType, t quarkusPackageType) bool { - for _, ti := range qt.PackageTypes { - if t == ti { - return true - } - } - return false - } - - if len(t.PackageTypes) == 0 && len(qt.PackageTypes) != 0 && !contains(qt.PackageTypes, fastJarPackageType) { + if len(t.PackageTypes) == 0 && len(qt.PackageTypes) != 0 && !containsPackageType(qt.PackageTypes, fastJarPackageType) { return false } @@ -110,7 +105,7 @@ types: if pt == fastJarPackageType && len(qt.PackageTypes) == 0 { continue } - if contains(qt.PackageTypes, pt) { + if containsPackageType(qt.PackageTypes, pt) { continue types } return false @@ -133,6 +128,27 @@ func (t *quarkusTrait) Configure(e *Environment) (bool, error) { func (t *quarkusTrait) Apply(e *Environment) error { if e.IntegrationInPhase(v1.IntegrationPhaseBuildingKit) { + if containsPackageType(t.PackageTypes, nativePackageType) { + // Native compilation is only supported for a subset of languages, + // so let's check for compatibility, and fail-fast the Integration, + // to save compute resources and user time. + for _, source := range e.Integration.Sources() { + if language := source.InferLanguage(); language != v1.LanguageKamelet && + language != v1.LanguageYaml && + language != v1.LanguageXML { + t.L.ForIntegration(e.Integration).Infof("Integration %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( + v1.IntegrationConditionKitAvailable, + corev1.ConditionFalse, + v1.IntegrationConditionUnsupportedLanguageReason, + fmt.Sprintf("native compilation for language %q is not supported", language)) + // Let the calling controller handle the Integration update + return nil + } + } + } + switch len(t.PackageTypes) { case 0: kit := t.newIntegrationKit(e, fastJarPackageType) @@ -293,3 +309,12 @@ func getBuilderTask(tasks []v1.Task) *v1.BuilderTask { } return nil } + +func containsPackageType(types []quarkusPackageType, t quarkusPackageType) bool { + for _, ti := range types { + if t == ti { + return true + } + } + return false +}