diff --git a/e2e/namespace/native/files/Java2.java b/e2e/namespace/native/files/Java2.java new file mode 100644 index 0000000000..8a3f7ad4ab --- /dev/null +++ b/e2e/namespace/native/files/Java2.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 Java2 extends RouteBuilder { + @Override + public void configure() throws Exception { + from("timer:tick") + .setHeader("m").constant("string!") + .setBody().simple("Magic2${header.m}") + .log("Java ${body}"); + } + } diff --git a/e2e/namespace/native/files/yaml2.yaml b/e2e/namespace/native/files/yaml2.yaml new file mode 100644 index 0000000000..d717e5da6b --- /dev/null +++ b/e2e/namespace/native/files/yaml2.yaml @@ -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. +# --------------------------------------------------------------------------- + +- from: + uri: "timer:yaml" + parameters: + period: "5000" + steps: + - set-header: + name: "m" + constant: "string!" + - set-body: + simple: "Magic${header.m}2" + - to: "log:info" diff --git a/e2e/namespace/native/native_test.go b/e2e/namespace/native/native_test.go index f1686f737a..51c10604d2 100644 --- a/e2e/namespace/native/native_test.go +++ b/e2e/namespace/native/native_test.go @@ -75,24 +75,10 @@ func TestNativeIntegrations(t *testing.T) { Expect(Kamel("delete", name, "-n", ns).Execute()).To(Succeed()) }) - t.Run("warm up before native build testing", func(t *testing.T) { - // The following native build test is under tight time constraints, so here it runs - // a warm up testing to make sure necessary jars are already downloaded. - name := "warm-up-yaml" - Expect(KamelRunWithID(operatorID, ns, "files/yaml.yaml", "--name", name).Execute()).To(Succeed()) - - Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) - Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort). - Should(Equal(corev1.ConditionTrue)) - Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) - - // Clean up - Expect(Kamel("delete", name, "-n", ns).Execute()).To(Succeed()) - Expect(DeleteKits(ns)).To(Succeed()) - }) - t.Run("automatic rollout deployment from fast-jar to native kit", func(t *testing.T) { - name := "jvm-to-native" + // Let's make sure we start from a clean state + Expect(DeleteKits(ns)).To(Succeed()) + name := "yaml-native" Expect(KamelRunWithID(operatorID, ns, "files/yaml.yaml", "--name", name, "-t", "quarkus.package-type=fast-jar", "-t", "quarkus.package-type=native", @@ -143,11 +129,24 @@ func TestNativeIntegrations(t *testing.T) { Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) + t.Run("yaml native should not rebuild", func(t *testing.T) { + name := "yaml-native-2" + Expect(KamelRunWithID(operatorID, ns, "files/yaml2.yaml", "--name", name, + "-t", "quarkus.package-type=native", + ).Execute()).To(Succeed()) + + // This one should run quickly as it suppose to reuse an IntegrationKit + Eventually(IntegrationPodPhase(ns, name), TestTimeoutShort).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("Magicstring!2")) + Eventually(IntegrationKit(ns, "yaml-native-2")).Should(Equal(IntegrationKit(ns, "yaml-native")())) + }) + // 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/e2e/namespace/native/native_with_sources_test.go b/e2e/namespace/native/native_with_sources_test.go index fc39ace542..61aff527b1 100644 --- a/e2e/namespace/native/native_with_sources_test.go +++ b/e2e/namespace/native/native_with_sources_test.go @@ -54,9 +54,39 @@ func TestNativeHighMemoryIntegrations(t *testing.T) { 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!")) + t.Run("java native same should not rebuild", func(t *testing.T) { + name := "java-native-clone" + Expect(KamelRunWithID(operatorID, ns, "files/Java.java", "--name", name, + "-t", "quarkus.package-type=native", + ).Execute()).To(Succeed()) + + // This one should run quickly as it suppose to reuse an IntegrationKit + Eventually(IntegrationPodPhase(ns, name), TestTimeoutShort).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!")) + Eventually(IntegrationKit(ns, "java-native-clone")).Should(Equal(IntegrationKit(ns, "java-native")())) + }) + + t.Run("java native should rebuild", func(t *testing.T) { + name := "java-native-2" + Expect(KamelRunWithID(operatorID, ns, "files/Java2.java", "--name", name, + "-t", "quarkus.package-type=native", + ).Execute()).To(Succeed()) + + Eventually(IntegrationPodPhase(ns, name), TestTimeoutVeryLong).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 Magic2string!")) + Eventually(IntegrationKit(ns, "java-native-2")).ShouldNot(Equal(IntegrationKit(ns, "java-native")())) + }) + // Clean up Expect(Kamel("delete", name, "-n", ns).Execute()).To(Succeed()) }) diff --git a/pkg/controller/integration/kits.go b/pkg/controller/integration/kits.go index 433d961801..5b464677d0 100644 --- a/pkg/controller/integration/kits.go +++ b/pkg/controller/integration/kits.go @@ -131,6 +131,12 @@ func integrationMatches(integration *v1.Integration, kit *v1.IntegrationKit) (bo ilog.Debug("Integration and integration-kit dependencies do not match", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace) return false, nil } + // If IntegrationKit has any source, we must verify that it corresponds with the one in the Integration. + // This is important in case of Native builds as we need to rebuild when language requires a source during build. + if (kit.Spec.Sources != nil && len(kit.Spec.Sources) > 0) && !hasMatchingSources(integration, kit) { + ilog.Debug("Integration and integration-kit sources do not match", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace) + return false, nil + } ilog.Debug("Matched Integration and integration-kit", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace) return true, nil @@ -250,3 +256,22 @@ func matchesTrait(it map[string]interface{}, kt map[string]interface{}) bool { // perform exact match on the two trait maps return reflect.DeepEqual(it, kt) } + +func hasMatchingSources(it *v1.Integration, kit *v1.IntegrationKit) bool { + if len(it.Sources()) != len(kit.Spec.Sources) { + return false + } + for _, itSource := range it.Sources() { + found := false + for _, ikSource := range kit.Spec.Sources { + if itSource.Content == ikSource.Content { + found = true + break + } + } + if !found { + return false + } + } + return true +} diff --git a/pkg/controller/integration/kits_test.go b/pkg/controller/integration/kits_test.go index 694be15219..72bfab91be 100644 --- a/pkg/controller/integration/kits_test.go +++ b/pkg/controller/integration/kits_test.go @@ -338,3 +338,100 @@ func TestHasMatchingTraits_KitSameTraitShouldBePicked(t *testing.T) { assert.Nil(t, err) assert.True(t, ok) } + +func TestHasMatchingSources(t *testing.T) { + integration := &v1.Integration{ + Spec: v1.IntegrationSpec{ + Sources: []v1.SourceSpec{ + v1.NewSourceSpec("test", "some content", v1.LanguageJavaShell), + }, + }, + } + + kit := &v1.IntegrationKit{ + Spec: v1.IntegrationKitSpec{ + Sources: []v1.SourceSpec{ + v1.NewSourceSpec("test", "some content", v1.LanguageJavaShell), + }, + }, + } + + hms := hasMatchingSources(integration, kit) + assert.True(t, hms) + + kit2 := &v1.IntegrationKit{ + Spec: v1.IntegrationKitSpec{ + Sources: []v1.SourceSpec{ + v1.NewSourceSpec("test", "some content 2", v1.LanguageJavaShell), + v1.NewSourceSpec("test", "some content", v1.LanguageJavaShell), + }, + }, + } + + hms2 := hasMatchingSources(integration, kit2) + assert.False(t, hms2) +} + +func TestHasMatchingMultipleSources(t *testing.T) { + integration := &v1.Integration{ + Spec: v1.IntegrationSpec{ + Sources: []v1.SourceSpec{ + v1.NewSourceSpec("test", "some content", v1.LanguageJavaShell), + v1.NewSourceSpec("test", "some content 2", v1.LanguageJavaShell), + }, + }, + } + + kit := &v1.IntegrationKit{ + Spec: v1.IntegrationKitSpec{ + Sources: []v1.SourceSpec{ + v1.NewSourceSpec("test", "some content 2", v1.LanguageJavaShell), + v1.NewSourceSpec("test", "some content", v1.LanguageJavaShell), + }, + }, + } + + hms := hasMatchingSources(integration, kit) + assert.True(t, hms) + + integration2 := &v1.Integration{ + Spec: v1.IntegrationSpec{ + Sources: []v1.SourceSpec{ + v1.NewSourceSpec("test", "some content", v1.LanguageJavaShell), + }, + }, + } + + hms2 := hasMatchingSources(integration2, kit) + assert.False(t, hms2) +} + +func TestHasNotMatchingSources(t *testing.T) { + integration := &v1.Integration{ + Spec: v1.IntegrationSpec{ + Sources: []v1.SourceSpec{ + v1.NewSourceSpec("test", "some content", v1.LanguageJavaShell), + }, + }, + } + + kit := &v1.IntegrationKit{ + Spec: v1.IntegrationKitSpec{ + Sources: []v1.SourceSpec{ + v1.NewSourceSpec("test", "some content 2", v1.LanguageJavaShell), + }, + }, + } + + hsm := hasMatchingSources(integration, kit) + assert.False(t, hsm) + + kit2 := &v1.IntegrationKit{ + Spec: v1.IntegrationKitSpec{ + Sources: []v1.SourceSpec{}, + }, + } + + hsm2 := hasMatchingSources(integration, kit2) + assert.False(t, hsm2) +} diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go index 23b682afd3..0511f9dd1c 100644 --- a/pkg/resources/resources.go +++ b/pkg/resources/resources.go @@ -167,6 +167,18 @@ var assets = func() http.FileSystem { name: "manager", modTime: time.Time{}, }, + "/manager/bundle": &vfsgen۰DirInfo{ + name: "bundle", + modTime: time.Time{}, + }, + "/manager/bundle/manifests": &vfsgen۰DirInfo{ + name: "manifests", + modTime: time.Time{}, + }, + "/manager/bundle/metadata": &vfsgen۰DirInfo{ + name: "metadata", + modTime: time.Time{}, + }, "/manager/operator-deployment.yaml": &vfsgen۰CompressedFileInfo{ name: "operator-deployment.yaml", modTime: time.Time{}, @@ -657,6 +669,7 @@ var assets = func() http.FileSystem { fs["/crd/bases/camel.apache.org_kamelets.yaml"].(os.FileInfo), } fs["/manager"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/manager/bundle"].(os.FileInfo), fs["/manager/operator-deployment.yaml"].(os.FileInfo), fs["/manager/operator-service-account.yaml"].(os.FileInfo), fs["/manager/patch-image-pull-policy-always.yaml"].(os.FileInfo), @@ -668,6 +681,10 @@ var assets = func() http.FileSystem { fs["/manager/patch-toleration.yaml"].(os.FileInfo), fs["/manager/patch-watch-namespace-global.yaml"].(os.FileInfo), } + fs["/manager/bundle"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/manager/bundle/manifests"].(os.FileInfo), + fs["/manager/bundle/metadata"].(os.FileInfo), + } fs["/prometheus"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ fs["/prometheus/operator-pod-monitor.yaml"].(os.FileInfo), fs["/prometheus/operator-prometheus-rule.yaml"].(os.FileInfo),