This is an automated email from the ASF dual-hosted git repository. pcongiusti pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 8a82e3fda8b213dbb47c90d7f6cf9123cc2e403a Author: Pasquale Congiusti <[email protected]> AuthorDate: Thu May 23 12:13:40 2024 +0200 feat(trait): jvm refactoring * add more unit test * enable the trait for synthetic Kits * add documentation on the jvm trait usage Closes #5476 --- docs/modules/ROOT/partials/apis/camel-k-crds.adoc | 11 +- docs/modules/traits/pages/builder.adoc | 3 +- docs/modules/traits/pages/jvm.adoc | 18 +- e2e/common/runtimes/runtimes_test.go | 6 +- e2e/common/traits/builder_test.go | 13 + e2e/support/test_support.go | 10 + helm/camel-k/crds/crd-integration-kit.yaml | 4 +- helm/camel-k/crds/crd-integration-platform.yaml | 26 +- helm/camel-k/crds/crd-integration-profile.yaml | 26 +- helm/camel-k/crds/crd-integration.yaml | 13 +- helm/camel-k/crds/crd-kamelet-binding.yaml | 14 +- helm/camel-k/crds/crd-pipe.yaml | 14 +- pkg/apis/camel/v1/trait/builder.go | 3 +- pkg/apis/camel/v1/trait/jvm.go | 3 + pkg/cmd/run_test.go | 4 +- .../bases/camel.apache.org_integrationkits.yaml | 4 +- .../camel.apache.org_integrationplatforms.yaml | 26 +- .../camel.apache.org_integrationprofiles.yaml | 26 +- .../crd/bases/camel.apache.org_integrations.yaml | 13 +- .../bases/camel.apache.org_kameletbindings.yaml | 14 +- .../config/crd/bases/camel.apache.org_pipes.yaml | 14 +- pkg/trait/jvm.go | 213 +++++++------ pkg/trait/jvm_test.go | 355 ++++++++++++++++++--- 23 files changed, 634 insertions(+), 199 deletions(-) diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc index b795ce1df..65d6a742a 100644 --- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc +++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc @@ -6234,7 +6234,8 @@ string | -Specify a base image +Specify a base image. In order to have the application working properly it must be a container image which has a Java JDK +installed and ready to use on path (ie `/usr/bin/java`). |`incrementalImageBuild` + bool @@ -7306,6 +7307,7 @@ bool Prints the command used the start the JVM in the container logs (default `true`) +Deprecated: no longer in use. |`debugAddress` + string @@ -7328,6 +7330,13 @@ string Additional JVM classpath (use `Linux` classpath separator) +|`jar` + +string +| + + +The Jar dependency which will run the application. Leave it empty for managed Integrations. + |=== diff --git a/docs/modules/traits/pages/builder.adoc b/docs/modules/traits/pages/builder.adoc index 7f3c280f3..221539ab4 100755 --- a/docs/modules/traits/pages/builder.adoc +++ b/docs/modules/traits/pages/builder.adoc @@ -45,7 +45,8 @@ Deprecated no longer in use | builder.base-image | string -| Specify a base image +| Specify a base image. In order to have the application working properly it must be a container image which has a Java JDK +installed and ready to use on path (ie `/usr/bin/java`). | builder.incremental-image-build | bool diff --git a/docs/modules/traits/pages/jvm.adoc b/docs/modules/traits/pages/jvm.adoc index a9b1ed480..20f7b2c63 100755 --- a/docs/modules/traits/pages/jvm.adoc +++ b/docs/modules/traits/pages/jvm.adoc @@ -40,6 +40,7 @@ The following configuration options are available: | jvm.print-command | bool | Prints the command used the start the JVM in the container logs (default `true`) +Deprecated: no longer in use. | jvm.debug-address | string @@ -53,11 +54,26 @@ The following configuration options are available: | string | Additional JVM classpath (use `Linux` classpath separator) +| jvm.jar +| string +| The Jar dependency which will run the application. Leave it empty for managed Integrations. + |=== // End of autogenerated code - DO NOT EDIT! (configuration) -== Examples +== Usage of jar parameters + +The `jar` parameter is something the user should not worry about, unless that, for any reason, he wants to specify which is the executable dependency to use. Mind that, in order to do that, the base image used to build the container require a java binary executable from path (ie, `/usr/bin/java`). + +This parameters enables also the possibility to use the trait when running a synthetic IntegrationKit (ie, "sourceless" Integrations). In such circumstances, the user can run a Camel application built externally and make use of the trait configuration as well as for example: + +[source,console] +$ kamel run --image docker.io/squakez/my-camel-sb:1.0.0 -t jvm.jar=/deployments/my-camel-app.jar -t jvm.options=-Xmx1024M + +The above command would allow the execution of the JVM trait given that the user specify the path to the jar to execute. + +== Other examples * Include an additional classpath to the `Integration`: + diff --git a/e2e/common/runtimes/runtimes_test.go b/e2e/common/runtimes/runtimes_test.go index dee3ae100..b6e2a3a34 100644 --- a/e2e/common/runtimes/runtimes_test.go +++ b/e2e/common/runtimes/runtimes_test.go @@ -54,7 +54,7 @@ func TestSourceLessIntegrations(t *testing.T) { g.Expect(KamelRunWithID(t, ctx, operatorID, ns, "--image", "docker.io/squakez/my-camel-main:1.0.0", "--resource", "configmap:my-cm-sourceless@/tmp/app/data").Execute()).To(Succeed()) g.Eventually(IntegrationPodPhase(t, ctx, ns, itName), TestTimeoutShort).Should(Equal(corev1.PodRunning)) g.Eventually(IntegrationConditionStatus(t, ctx, ns, itName, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) - g.Eventually(IntegrationCondition(t, ctx, ns, itName, v1.IntegrationConditionType("JVMTraitInfo"))().Message).Should(Equal("explicitly disabled by the platform: integration kit was not created via Camel K operator")) + g.Eventually(IntegrationCondition(t, ctx, ns, itName, v1.IntegrationConditionType("JVMTraitInfo"))().Message).Should(ContainSubstring("explicitly disabled by the platform")) g.Eventually(IntegrationLogs(t, ctx, ns, itName), TestTimeoutShort).Should(ContainSubstring(cmData["my-file.txt"])) g.Eventually(IntegrationLogs(t, ctx, ns, itName), TestTimeoutShort).Should(ContainSubstring("Apache Camel (Main)")) }) @@ -64,7 +64,7 @@ func TestSourceLessIntegrations(t *testing.T) { g.Expect(KamelRunWithID(t, ctx, operatorID, ns, "--image", "docker.io/squakez/my-camel-sb:1.0.0", "--resource", "configmap:my-cm-sourceless@/tmp/app/data").Execute()).To(Succeed()) g.Eventually(IntegrationPodPhase(t, ctx, ns, itName), TestTimeoutShort).Should(Equal(corev1.PodRunning)) g.Eventually(IntegrationConditionStatus(t, ctx, ns, itName, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) - g.Eventually(IntegrationCondition(t, ctx, ns, itName, v1.IntegrationConditionType("JVMTraitInfo"))().Message).Should(Equal("explicitly disabled by the platform: integration kit was not created via Camel K operator")) + g.Eventually(IntegrationCondition(t, ctx, ns, itName, v1.IntegrationConditionType("JVMTraitInfo"))().Message).Should(ContainSubstring("explicitly disabled by the platform")) g.Eventually(IntegrationLogs(t, ctx, ns, itName), TestTimeoutShort).Should(ContainSubstring(cmData["my-file.txt"])) g.Eventually(IntegrationLogs(t, ctx, ns, itName), TestTimeoutShort).Should(ContainSubstring("Spring Boot")) }) @@ -74,7 +74,7 @@ func TestSourceLessIntegrations(t *testing.T) { g.Expect(KamelRunWithID(t, ctx, operatorID, ns, "--image", "docker.io/squakez/my-camel-quarkus:1.0.0", "--resource", "configmap:my-cm-sourceless@/tmp/app/data").Execute()).To(Succeed()) g.Eventually(IntegrationPodPhase(t, ctx, ns, itName), TestTimeoutShort).Should(Equal(corev1.PodRunning)) g.Eventually(IntegrationConditionStatus(t, ctx, ns, itName, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) - g.Eventually(IntegrationCondition(t, ctx, ns, itName, v1.IntegrationConditionType("JVMTraitInfo"))().Message).Should(Equal("explicitly disabled by the platform: integration kit was not created via Camel K operator")) + g.Eventually(IntegrationCondition(t, ctx, ns, itName, v1.IntegrationConditionType("JVMTraitInfo"))().Message).Should(ContainSubstring("explicitly disabled by the platform")) g.Eventually(IntegrationLogs(t, ctx, ns, itName), TestTimeoutShort).Should(ContainSubstring(cmData["my-file.txt"])) g.Eventually(IntegrationLogs(t, ctx, ns, itName), TestTimeoutShort).Should(ContainSubstring("powered by Quarkus")) }) diff --git a/e2e/common/traits/builder_test.go b/e2e/common/traits/builder_test.go index b329f5c2d..db8d42a00 100644 --- a/e2e/common/traits/builder_test.go +++ b/e2e/common/traits/builder_test.go @@ -266,6 +266,19 @@ func TestBuilderTrait(t *testing.T) { g.Expect(TestClient(t).Delete(ctx, mavenProfile1Cm)).To(Succeed()) g.Expect(TestClient(t).Delete(ctx, mavenProfile2Cm)).To(Succeed()) }) + + t.Run("Run distroless container image", func(t *testing.T) { + name := RandomizedSuffixName("java") + g.Expect(KamelRunWithID(t, ctx, operatorID, ns, "files/Java.java", "--name", name, "-t", "builder.base-image=gcr.io/distroless/java17-debian12").Execute()).To(Succeed()) + + g.Eventually(IntegrationPodPhase(t, ctx, ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + g.Eventually(IntegrationConditionStatus(t, ctx, ns, name, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue)) + g.Eventually(IntegrationLogs(t, ctx, ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) + integrationKitName := IntegrationKit(t, ctx, ns, name)() + g.Eventually(KitRootImage(t, ctx, ns, integrationKitName), TestTimeoutShort).Should(Equal("gcr.io/distroless/java17-debian12")) + + g.Expect(Kamel(t, ctx, "delete", "--all", "-n", ns).Execute()).To(Succeed()) + }) }) } diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go index 16a7d2046..4f7d950b0 100644 --- a/e2e/support/test_support.go +++ b/e2e/support/test_support.go @@ -1184,6 +1184,16 @@ func KitImage(t *testing.T, ctx context.Context, ns, name string) func() string } } +func KitRootImage(t *testing.T, ctx context.Context, ns, name string) func() string { + return func() string { + kit := Kit(t, ctx, ns, name)() + if kit == nil { + return "" + } + return kit.Status.RootImage + } +} + func KitCondition(t *testing.T, ctx context.Context, ns string, name string, conditionType v1.IntegrationKitConditionType) func() *v1.IntegrationKitCondition { return func() *v1.IntegrationKitCondition { kt := Kit(t, ctx, ns, name)() diff --git a/helm/camel-k/crds/crd-integration-kit.yaml b/helm/camel-k/crds/crd-integration-kit.yaml index 82f31750c..f21dfc47f 100644 --- a/helm/camel-k/crds/crd-integration-kit.yaml +++ b/helm/camel-k/crds/crd-integration-kit.yaml @@ -213,7 +213,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: diff --git a/helm/camel-k/crds/crd-integration-platform.yaml b/helm/camel-k/crds/crd-integration-platform.yaml index ed67e6cd8..eeeec4364 100644 --- a/helm/camel-k/crds/crd-integration-platform.yaml +++ b/helm/camel-k/crds/crd-integration-platform.yaml @@ -528,7 +528,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -1285,14 +1287,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: @@ -2518,7 +2525,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -3275,14 +3284,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: diff --git a/helm/camel-k/crds/crd-integration-profile.yaml b/helm/camel-k/crds/crd-integration-profile.yaml index 8374b1ed0..c827dbd52 100644 --- a/helm/camel-k/crds/crd-integration-profile.yaml +++ b/helm/camel-k/crds/crd-integration-profile.yaml @@ -405,7 +405,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -1162,14 +1164,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: @@ -2278,7 +2285,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -3035,14 +3044,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: diff --git a/helm/camel-k/crds/crd-integration.yaml b/helm/camel-k/crds/crd-integration.yaml index 2be45505b..808ad117f 100644 --- a/helm/camel-k/crds/crd-integration.yaml +++ b/helm/camel-k/crds/crd-integration.yaml @@ -6466,7 +6466,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -7223,14 +7225,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: diff --git a/helm/camel-k/crds/crd-kamelet-binding.yaml b/helm/camel-k/crds/crd-kamelet-binding.yaml index 9c5bb4500..40f60e223 100644 --- a/helm/camel-k/crds/crd-kamelet-binding.yaml +++ b/helm/camel-k/crds/crd-kamelet-binding.yaml @@ -6742,7 +6742,10 @@ spec: use for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the + application working properly it must be a container + image which has a Java JDK installed and ready to use + on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -7507,14 +7510,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM - in the container logs (default `true`) + description: 'Prints the command used the start the JVM + in the container logs (default `true`) Deprecated: no + longer in use.' type: boolean type: object kamelets: diff --git a/helm/camel-k/crds/crd-pipe.yaml b/helm/camel-k/crds/crd-pipe.yaml index b0ffe4f17..e1d2f7fd2 100644 --- a/helm/camel-k/crds/crd-pipe.yaml +++ b/helm/camel-k/crds/crd-pipe.yaml @@ -6740,7 +6740,10 @@ spec: use for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the + application working properly it must be a container + image which has a Java JDK installed and ready to use + on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -7505,14 +7508,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM - in the container logs (default `true`) + description: 'Prints the command used the start the JVM + in the container logs (default `true`) Deprecated: no + longer in use.' type: boolean type: object kamelets: diff --git a/pkg/apis/camel/v1/trait/builder.go b/pkg/apis/camel/v1/trait/builder.go index 0eab4cf8c..12054f95d 100644 --- a/pkg/apis/camel/v1/trait/builder.go +++ b/pkg/apis/camel/v1/trait/builder.go @@ -31,7 +31,8 @@ type BuilderTrait struct { // The strategy to use, either `pod` or `routine` (default `routine`) // +kubebuilder:validation:Enum=pod;routine Strategy string `property:"strategy" json:"strategy,omitempty"` - // Specify a base image + // Specify a base image. In order to have the application working properly it must be a container image which has a Java JDK + // installed and ready to use on path (ie `/usr/bin/java`). BaseImage string `property:"base-image" json:"baseImage,omitempty"` // Use the incremental image build option, to reuse existing containers (default `true`) IncrementalImageBuild *bool `property:"incremental-image-build" json:"incrementalImageBuild,omitempty"` diff --git a/pkg/apis/camel/v1/trait/jvm.go b/pkg/apis/camel/v1/trait/jvm.go index abe1e1185..4cc5b8c1a 100644 --- a/pkg/apis/camel/v1/trait/jvm.go +++ b/pkg/apis/camel/v1/trait/jvm.go @@ -29,6 +29,7 @@ type JVMTrait struct { // Suspends the target JVM immediately before the main class is loaded DebugSuspend *bool `property:"debug-suspend" json:"debugSuspend,omitempty"` // Prints the command used the start the JVM in the container logs (default `true`) + // Deprecated: no longer in use. PrintCommand *bool `property:"print-command" json:"printCommand,omitempty"` // Transport address at which to listen for the newly launched JVM (default `*:5005`) DebugAddress string `property:"debug-address" json:"debugAddress,omitempty"` @@ -36,4 +37,6 @@ type JVMTrait struct { Options []string `property:"options" json:"options,omitempty"` // Additional JVM classpath (use `Linux` classpath separator) Classpath string `property:"classpath" json:"classpath,omitempty"` + // The Jar dependency which will run the application. Leave it empty for managed Integrations. + Jar string `property:"jar" json:"jar,omitempty"` } diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go index 625a850d4..8ba5399b8 100644 --- a/pkg/cmd/run_test.go +++ b/pkg/cmd/run_test.go @@ -437,7 +437,6 @@ func TestConfigureTraits(t *testing.T) { _, err := test.ExecuteCommand(rootCmd, "run", "--trait", "affinity.pod-affinity=false", "--trait", "environment.container-meta=false", - "--trait", "jvm.print-command=false", "--trait", "prometheus.pod-monitor=false", "example.js") if err != nil { @@ -455,10 +454,9 @@ func TestConfigureTraits(t *testing.T) { require.NoError(t, err) traitMap, err := trait.ToTraitMap(traits) require.NoError(t, err) - assert.Len(t, traitMap, 4) + assert.Len(t, traitMap, 3) assertTraitConfiguration(t, traits.Affinity, &traitv1.AffinityTrait{PodAffinity: pointer.Bool(false)}) assertTraitConfiguration(t, traits.Environment, &traitv1.EnvironmentTrait{ContainerMeta: pointer.Bool(false)}) - assertTraitConfiguration(t, traits.JVM, &traitv1.JVMTrait{PrintCommand: pointer.Bool(false)}) assertTraitConfiguration(t, traits.Prometheus, &traitv1.PrometheusTrait{PodMonitor: pointer.Bool(false)}) } diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrationkits.yaml b/pkg/resources/config/crd/bases/camel.apache.org_integrationkits.yaml index 82f31750c..f21dfc47f 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrationkits.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationkits.yaml @@ -213,7 +213,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml index ed67e6cd8..eeeec4364 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml @@ -528,7 +528,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -1285,14 +1287,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: @@ -2518,7 +2525,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -3275,14 +3284,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml index 8374b1ed0..c827dbd52 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml @@ -405,7 +405,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -1162,14 +1164,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: @@ -2278,7 +2285,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -3035,14 +3044,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml index 2be45505b..808ad117f 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml @@ -6466,7 +6466,9 @@ spec: for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the application + working properly it must be a container image which has + a Java JDK installed and ready to use on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -7223,14 +7225,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM in - the container logs (default `true`) + description: 'Prints the command used the start the JVM in + the container logs (default `true`) Deprecated: no longer + in use.' type: boolean type: object kamelets: diff --git a/pkg/resources/config/crd/bases/camel.apache.org_kameletbindings.yaml b/pkg/resources/config/crd/bases/camel.apache.org_kameletbindings.yaml index 9c5bb4500..40f60e223 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_kameletbindings.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_kameletbindings.yaml @@ -6742,7 +6742,10 @@ spec: use for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the + application working properly it must be a container + image which has a Java JDK installed and ready to use + on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -7507,14 +7510,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM - in the container logs (default `true`) + description: 'Prints the command used the start the JVM + in the container logs (default `true`) Deprecated: no + longer in use.' type: boolean type: object kamelets: diff --git a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml index b0ffe4f17..e1d2f7fd2 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml @@ -6740,7 +6740,10 @@ spec: use for the builder pod. type: object baseImage: - description: Specify a base image + description: Specify a base image. In order to have the + application working properly it must be a container + image which has a Java JDK installed and ready to use + on path (ie `/usr/bin/java`). type: string configuration: description: 'Legacy trait configuration parameters. Deprecated: @@ -7505,14 +7508,19 @@ spec: description: Can be used to enable or disable a trait. All traits share this common property. type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string options: description: A list of JVM options items: type: string type: array printCommand: - description: Prints the command used the start the JVM - in the container logs (default `true`) + description: 'Prints the command used the start the JVM + in the container logs (default `true`) Deprecated: no + longer in use.' type: boolean type: object kamelets: diff --git a/pkg/trait/jvm.go b/pkg/trait/jvm.go index bef632904..87f3da133 100644 --- a/pkg/trait/jvm.go +++ b/pkg/trait/jvm.go @@ -24,8 +24,6 @@ import ( "sort" "strings" - "github.com/apache/camel-k/v2/pkg/util/boolean" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -62,14 +60,15 @@ func newJvmTrait() Trait { BaseTrait: NewBaseTrait(jvmTraitID, jvmTraitOrder), JVMTrait: traitv1.JVMTrait{ DebugAddress: "*:5005", - PrintCommand: pointer.Bool(true), }, } } func (t *jvmTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + // Deprecated: the JVM has to be a platform trait and the user should not be able to disable it if !pointer.BoolDeref(t.Enabled, true) { - return false, NewIntegrationConditionUserDisabled("JVM"), nil + notice := userDisabledMessage + "; this configuration is deprecated and may be removed within next releases" + return false, NewIntegrationCondition("JVM", v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationReason, notice), nil } if !e.IntegrationKitInPhase(v1.IntegrationKitPhaseReady) || !e.IntegrationInRunningPhases() { return false, nil, nil @@ -82,19 +81,18 @@ func (t *jvmTrait) Configure(e *Environment) (bool, *TraitCondition, error) { } } - if e.IntegrationKit != nil && e.IntegrationKit.IsSynthetic() { - return false, NewIntegrationConditionPlatformDisabledWithMessage("JVM", "integration kit was not created via Camel K operator"), nil + if e.IntegrationKit != nil && e.IntegrationKit.IsSynthetic() && t.Jar == "" { + // We skip this trait since we cannot make any assumption on the container Java tooling running + // for the synthetic IntegrationKit + return false, NewIntegrationConditionPlatformDisabledWithMessage( + "JVM", + "integration kit was not created via Camel K operator and the user did not provide the jar to execute", + ), nil } - if e.CamelCatalog == nil { - return false, NewIntegrationConditionPlatformDisabledCatalogMissing(), nil - } return true, nil, nil } -// TODO: refactor the code -// -//nolint:maintidx func (t *jvmTrait) Apply(e *Environment) error { kit := e.IntegrationKit @@ -115,75 +113,120 @@ func (t *jvmTrait) Apply(e *Environment) error { return fmt.Errorf("unable to find integration kit for integration %s", e.Integration.Name) } - classpath := sets.NewSet() - - classpath.Add("./resources") - classpath.Add(filepath.ToSlash(camel.ConfigResourcesMountPath)) - classpath.Add(filepath.ToSlash(camel.ResourcesDefaultMountPath)) - if t.Classpath != "" { - classpath.Add(strings.Split(t.Classpath, ":")...) - } - - for _, artifact := range kit.Status.Artifacts { - classpath.Add(artifact.Target) - } - - if kit.IsExternal() { - // In case of an external created kit, we do not have any information about - // the classpath, so we assume the all jars in /deployments/dependencies/ have - // to be taken into account. - dependencies := filepath.Join(builder.DeploymentDir, builder.DependenciesDir) - classpath.Add( - dependencies+"/*", - dependencies+"/app/*", - dependencies+"/lib/boot/*", - dependencies+"/lib/main/*", - dependencies+"/quarkus/*", - ) - } - container := e.GetIntegrationContainer() if container == nil { - return fmt.Errorf("unable to find integration container: %s", e.Integration.Name) + return fmt.Errorf("unable to find a container for %s Integration", e.Integration.Name) } // Build the container command // Other traits may have already contributed some arguments args := container.Args - // Remote debugging if pointer.BoolDeref(t.Debug, false) { - suspend := "n" - if pointer.BoolDeref(t.DebugSuspend, false) { - suspend = "y" - } - args = append(args, - fmt.Sprintf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=%s,address=%s", - suspend, t.DebugAddress)) - - // Add label to mark the pods with debug enabled - e.Resources.VisitPodTemplateMeta(func(meta *metav1.ObjectMeta) { - if meta.Labels == nil { - meta.Labels = make(map[string]string) - } - meta.Labels["camel.apache.org/debug"] = boolean.TrueString - }) + debugArgs := t.enableDebug(e) + args = append(args, debugArgs) } hasHeapSizeOption := false // Add JVM options if len(t.Options) > 0 { hasHeapSizeOption = util.StringSliceContainsAnyOf(t.Options, "-Xmx", "-XX:MaxHeapSize", "-XX:MinRAMPercentage", "-XX:MaxRAMPercentage") - args = append(args, t.Options...) } - // Translate HTTP proxy environment variables, that are set by the environment trait, - // into corresponding JVM system properties. + // Tune JVM maximum heap size based on the container memory limit, if any. + // This is configured off-container, thus is limited to explicit user configuration. + // We may want to inject a wrapper script into the container image, so that it can + // be performed in-container, based on CGroups memory resource control files. + if memory, hasLimit := container.Resources.Limits[corev1.ResourceMemory]; !hasHeapSizeOption && hasLimit { + // Simple heuristic that caps the maximum heap size to 50% of the memory limit + percentage := defaultMaxMemoryPercentage + // Unless the memory limit is lower than 300M, in which case we leave more room for the non-heap memory + if resource.NewScaledQuantity(lowMemoryThreshold, defaultMaxMemoryScale).Cmp(memory) > 0 { + percentage = lowMemoryMAxMemoryDefaultPercentage + } + //nolint:mnd + memScaled := memory.ScaledValue(resource.Mega) * percentage / 100 + args = append(args, fmt.Sprintf("-Xmx%dM", memScaled)) + } + + httpProxyArgs, err := t.prepareHTTPProxy(container) + if err != nil { + return err + } + if httpProxyArgs != nil { + args = append(args, httpProxyArgs...) + } + + // If user provided the jar, we will execute on the container something like + // java -Dxyx ... -cp ... -jar my-app.jar + // For this reason it's imporant that the container is a java based container able to run a Camel (hence Java) application + container.WorkingDir = builder.DeploymentDir + container.Command = []string{"java"} + classpathItems := t.prepareClasspathItems(container) + if t.Jar != "" { + // User is providing the Jar to execute explicitly + args = append(args, "-cp", strings.Join(classpathItems, ":")) + args = append(args, "-jar", t.Jar) + } else { + if e.CamelCatalog == nil { + return fmt.Errorf("cannot execute trait: missing Camel catalog") + } + kitDepsDirs := getKitDependenciesDirectories(kit) + classpathItems = append(classpathItems, kitDepsDirs...) + args = append(args, "-cp", strings.Join(classpathItems, ":")) + args = append(args, e.CamelCatalog.Runtime.ApplicationClass) + } + container.Args = args + + return nil +} + +func (t *jvmTrait) enableDebug(e *Environment) string { + suspend := "n" + if pointer.BoolDeref(t.DebugSuspend, false) { + suspend = "y" + } + // Add label to mark the pods with debug enabled + e.Resources.VisitPodTemplateMeta(func(meta *metav1.ObjectMeta) { + if meta.Labels == nil { + meta.Labels = make(map[string]string) + } + meta.Labels["camel.apache.org/debug"] = "true" + }) + + return fmt.Sprintf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=%s,address=%s", + suspend, t.DebugAddress) +} + +func (t *jvmTrait) prepareClasspathItems(container *corev1.Container) []string { + classpath := sets.NewSet() + classpath.Add("./resources") + classpath.Add(filepath.ToSlash(camel.ConfigResourcesMountPath)) + classpath.Add(filepath.ToSlash(camel.ResourcesDefaultMountPath)) + if t.Classpath != "" { + classpath.Add(strings.Split(t.Classpath, ":")...) + } + // Add mounted resources to the class path + for _, m := range container.VolumeMounts { + classpath.Add(m.MountPath) + } + items := classpath.List() + // Keep class path sorted so that it's consistent over reconciliation cycles + sort.Strings(items) + + return items +} + +// Translate HTTP proxy environment variables, that are set by the environment trait, +// into corresponding JVM system properties. +func (t *jvmTrait) prepareHTTPProxy(container *corev1.Container) ([]string, error) { + var args []string + if HTTPProxy := envvar.Get(container.Env, "HTTP_PROXY"); HTTPProxy != nil { u, err := url.Parse(HTTPProxy.Value) if err != nil { - return err + return args, err } if !util.StringSliceContainsAnyOf(t.Options, "http.proxyHost") { args = append(args, fmt.Sprintf("-Dhttp.proxyHost=%q", u.Hostname())) @@ -202,7 +245,7 @@ func (t *jvmTrait) Apply(e *Environment) error { if HTTPSProxy := envvar.Get(container.Env, "HTTPS_PROXY"); HTTPSProxy != nil { u, err := url.Parse(HTTPSProxy.Value) if err != nil { - return err + return args, err } if !util.StringSliceContainsAnyOf(t.Options, "https.proxyHost") { args = append(args, fmt.Sprintf("-Dhttps.proxyHost=%q", u.Hostname())) @@ -231,44 +274,18 @@ func (t *jvmTrait) Apply(e *Environment) error { } } - // Tune JVM maximum heap size based on the container memory limit, if any. - // This is configured off-container, thus is limited to explicit user configuration. - // We may want to inject a wrapper script into the container image, so that it can - // be performed in-container, based on CGroups memory resource control files. - if memory, hasLimit := container.Resources.Limits[corev1.ResourceMemory]; !hasHeapSizeOption && hasLimit { - // Simple heuristic that caps the maximum heap size to 50% of the memory limit - percentage := defaultMaxMemoryPercentage - // Unless the memory limit is lower than 300M, in which case we leave more room for the non-heap memory - if resource.NewScaledQuantity(lowMemoryThreshold, defaultMaxMemoryScale).Cmp(memory) > 0 { - percentage = lowMemoryMAxMemoryDefaultPercentage - } - //nolint:mnd - memScaled := memory.ScaledValue(resource.Mega) * percentage / 100 - args = append(args, fmt.Sprintf("-Xmx%dM", memScaled)) - } - - // Add mounted resources to the class path - for _, m := range container.VolumeMounts { - classpath.Add(m.MountPath) - } - items := classpath.List() - // Keep class path sorted so that it's consistent over reconciliation cycles - sort.Strings(items) - args = append(args, "-cp", strings.Join(items, ":")) - - args = append(args, e.CamelCatalog.Runtime.ApplicationClass) + return args, nil +} - if pointer.BoolDeref(t.PrintCommand, false) { - args = append([]string{"exec", "java"}, args...) - container.Command = []string{"/bin/sh", "-c"} - cmd := strings.Join(args, " ") - container.Args = []string{"echo " + cmd + " && " + cmd} - } else { - container.Command = []string{"java"} - container.Args = args +// getKitDependenciesDirectories returns the list of directories, scanning the dependencies list. +func getKitDependenciesDirectories(kit *v1.IntegrationKit) []string { + s := sets.NewSet() + for _, dep := range kit.Status.Artifacts { + path := filepath.Dir(dep.Target) + s.Add(fmt.Sprintf("%s/*", path)) } + values := s.List() + sort.Strings(values) - container.WorkingDir = builder.DeploymentDir - - return nil + return values } diff --git a/pkg/trait/jvm_test.go b/pkg/trait/jvm_test.go index 3e3906d13..6f04f549e 100644 --- a/pkg/trait/jvm_test.go +++ b/pkg/trait/jvm_test.go @@ -20,26 +20,20 @@ package trait import ( "fmt" "path/filepath" - "sort" "strings" "testing" + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + "github.com/apache/camel-k/v2/pkg/util/camel" + "github.com/apache/camel-k/v2/pkg/util/kubernetes" + "github.com/apache/camel-k/v2/pkg/util/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" - serving "knative.dev/serving/pkg/apis/serving/v1" - - v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" - "github.com/apache/camel-k/v2/pkg/builder" - "github.com/apache/camel-k/v2/pkg/util/camel" - "github.com/apache/camel-k/v2/pkg/util/kubernetes" - "github.com/apache/camel-k/v2/pkg/util/sets" - "github.com/apache/camel-k/v2/pkg/util/test" ) var ( @@ -85,7 +79,7 @@ func TestConfigureJvmTraitInWrongJvmDisabled(t *testing.T) { v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, "TraitConfiguration", - "explicitly disabled by the user", + "explicitly disabled by the user; this configuration is deprecated and may be removed within next releases", ) configured, condition, err := trait.Configure(environment) require.NoError(t, err) @@ -94,6 +88,169 @@ func TestConfigureJvmTraitInWrongJvmDisabled(t *testing.T) { assert.Equal(t, expectedCondition, condition) } +func TestConfigureJvmTraitExecutableSourcelessContainer(t *testing.T) { + trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) + environment.IntegrationKit.Labels[v1.IntegrationKitTypeLabel] = v1.IntegrationKitTypeSynthetic + + configured, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.False(t, configured) + assert.Equal(t, + "explicitly disabled by the platform: integration kit was not created via Camel K operator and the user did not provide the jar to execute", + condition.message, + ) +} + +func TestConfigureJvmTraitExecutableSourcelessContainerWithJar(t *testing.T) { + trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) + environment.IntegrationKit.Labels[v1.IntegrationKitTypeLabel] = v1.IntegrationKitTypeSynthetic + trait.Jar = "my-path/to/my-app.jar" + + d := appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: defaultContainerName, + }, + }, + }, + }, + }, + } + + environment.Resources.Add(&d) + configured, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.True(t, configured) + assert.Nil(t, condition) + + err = trait.Apply(environment) + + require.NoError(t, err) + assert.Equal(t, []string{ + "-cp", + fmt.Sprintf("./resources:%s:%s", crMountPath, rdMountPath), + "-jar", "my-path/to/my-app.jar", + }, d.Spec.Template.Spec.Containers[0].Args) +} + +func TestConfigureJvmTraitExecutableSourcelessContainerWithJarAndOptions(t *testing.T) { + trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) + environment.IntegrationKit.Labels[v1.IntegrationKitTypeLabel] = v1.IntegrationKitTypeSynthetic + trait.Jar = "my-path/to/my-app.jar" + // Add some additional JVM configurations + trait.Classpath = "deps/a.jar:deps/b.jar" + trait.Options = []string{ + "-Xmx1234M", + "-Dmy-prop=abc", + } + + d := appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: defaultContainerName, + }, + }, + }, + }, + }, + } + + environment.Resources.Add(&d) + configured, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.True(t, configured) + assert.Nil(t, condition) + + err = trait.Apply(environment) + + require.NoError(t, err) + assert.Equal(t, []string{ + "-Xmx1234M", "-Dmy-prop=abc", + "-cp", "./resources:/etc/camel/conf.d/_resources:/etc/camel/resources:deps/a.jar:deps/b.jar", + "-jar", "my-path/to/my-app.jar", + }, d.Spec.Template.Spec.Containers[0].Args) +} + +func TestConfigureJvmTraitWithJar(t *testing.T) { + trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.Jar = "my-path/to/my-app.jar" + + d := appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: defaultContainerName, + }, + }, + }, + }, + }, + } + + environment.Resources.Add(&d) + configured, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.True(t, configured) + assert.Nil(t, condition) + + err = trait.Apply(environment) + + require.NoError(t, err) + assert.Equal(t, []string{ + "-cp", + fmt.Sprintf("./resources:%s:%s", crMountPath, rdMountPath), + "-jar", "my-path/to/my-app.jar", + }, d.Spec.Template.Spec.Containers[0].Args) +} + +func TestConfigureJvmTraitWithJarAndConfigs(t *testing.T) { + trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) + trait.Jar = "my-path/to/my-app.jar" + // Add some additional JVM configurations + trait.Classpath = "deps/a.jar:deps/b.jar" + trait.Options = []string{ + "-Xmx1234M", + "-Dmy-prop=abc", + } + + d := appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: defaultContainerName, + }, + }, + }, + }, + }, + } + + environment.Resources.Add(&d) + configured, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.True(t, configured) + assert.Nil(t, condition) + + err = trait.Apply(environment) + + require.NoError(t, err) + assert.Equal(t, []string{ + "-Xmx1234M", "-Dmy-prop=abc", + "-cp", "./resources:/etc/camel/conf.d/_resources:/etc/camel/resources:deps/a.jar:deps/b.jar", + "-jar", "my-path/to/my-app.jar", + }, d.Spec.Template.Spec.Containers[0].Args) +} + func TestConfigureJvmTraitInWrongIntegrationKitPhaseExternal(t *testing.T) { trait, environment := createNominalJvmTest(v1.IntegrationKitTypeSynthetic) @@ -102,7 +259,7 @@ func TestConfigureJvmTraitInWrongIntegrationKitPhaseExternal(t *testing.T) { v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, "TraitConfiguration", - "explicitly disabled by the platform: integration kit was not created via Camel K operator", + "explicitly disabled by the platform: integration kit was not created via Camel K operator and the user did not provide the jar to execute", ) configured, condition, err := trait.Configure(environment) require.NoError(t, err) @@ -134,16 +291,13 @@ func TestApplyJvmTraitWithDeploymentResource(t *testing.T) { } environment.Resources.Add(&d) - - err := trait.Apply(environment) - + configure, condition, err := trait.Configure(environment) require.NoError(t, err) + assert.True(t, configure) + assert.Nil(t, condition) + err = trait.Apply(environment) - s := sets.NewSet() - s.Add("./resources", crMountPath, rdMountPath, "/mount/path") - cp := s.List() - sort.Strings(cp) - + require.NoError(t, err) assert.Equal(t, []string{ "-cp", fmt.Sprintf("./resources:%s:%s:/mount/path", crMountPath, rdMountPath), @@ -168,16 +322,13 @@ func TestApplyJvmTraitWithKNativeResource(t *testing.T) { } environment.Resources.Add(&s) - - err := trait.Apply(environment) - + configure, condition, err := trait.Configure(environment) require.NoError(t, err) + assert.True(t, configure) + assert.Nil(t, condition) + err = trait.Apply(environment) - st := sets.NewSet() - st.Add("./resources", crMountPath, rdMountPath, "/mount/path") - cp := st.List() - sort.Strings(cp) - + require.NoError(t, err) assert.Equal(t, []string{ "-cp", fmt.Sprintf("./resources:%s:%s:/mount/path", crMountPath, rdMountPath), @@ -210,11 +361,9 @@ func TestApplyJvmTraitWithDebugEnabled(t *testing.T) { } environment.Resources.Add(&d) - err := trait.Apply(environment) require.NoError(t, err) - assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005", ) @@ -239,22 +388,19 @@ func TestApplyJvmTraitWithExternalKitType(t *testing.T) { environment.Resources.Add(&d) - err := trait.Apply(environment) + environment.Resources.Add(&d) + configure, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.True(t, configure) + assert.Nil(t, condition) + err = trait.Apply(environment) require.NoError(t, err) - container := environment.GetIntegrationContainer() - - assert.Equal(t, 3, len(container.Args)) - assert.Equal(t, "-cp", container.Args[0]) - - // classpath JAR location segments must be wildcarded for an external kit - for _, cp := range strings.Split(container.Args[1], ":") { - if strings.HasPrefix(cp, builder.DeploymentDir) { - assert.True(t, strings.HasSuffix(cp, "/*")) - } - } - - assert.Equal(t, "io.quarkus.bootstrap.runner.QuarkusEntryPoint", container.Args[2]) + assert.Equal(t, []string{ + "-cp", + fmt.Sprintf("./resources:%s:%s", crMountPath, rdMountPath), + "io.quarkus.bootstrap.runner.QuarkusEntryPoint", + }, d.Spec.Template.Spec.Containers[0].Args) } func TestApplyJvmTraitWithClasspath(t *testing.T) { @@ -278,17 +424,109 @@ func TestApplyJvmTraitWithClasspath(t *testing.T) { }, }, } + environment.Resources.Add(&d) - err := trait.Apply(environment) + configure, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.True(t, configure) + assert.Nil(t, condition) + err = trait.Apply(environment) require.NoError(t, err) assert.Equal(t, []string{ "-cp", - fmt.Sprintf("./resources:%s:%s:/mount/path:%s:%s", crMountPath, rdMountPath, - "/path/to/another/dep.jar", "/path/to/my-dep.jar"), + fmt.Sprintf("./resources:%s:%s:/mount/path:%s:%s", crMountPath, rdMountPath, "/path/to/another/dep.jar", "/path/to/my-dep.jar"), "io.quarkus.bootstrap.runner.QuarkusEntryPoint", }, d.Spec.Template.Spec.Containers[0].Args) } +func TestApplyJvmTraitKitMissing(t *testing.T) { + trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) + environment.IntegrationKit = nil + + err := trait.Apply(environment) + + require.Error(t, err) + assert.True(t, strings.HasPrefix(err.Error(), "unable to find integration kit")) +} + +func TestApplyJvmTraitContainerResourceArgs(t *testing.T) { + trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) + memoryLimit := make(corev1.ResourceList) + memoryLimit, err := kubernetes.ConfigureResource("4Gi", memoryLimit, corev1.ResourceMemory) + require.NoError(t, err) + d := appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: defaultContainerName, + Resources: corev1.ResourceRequirements{ + Limits: memoryLimit, + }, + }, + }, + }, + }, + }, + } + environment.Resources.Add(&d) + err = trait.Apply(environment) + + require.NoError(t, err) + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Xmx2147M") + + // User specified Xmx option + trait.Options = []string{"-Xmx1111M"} + err = trait.Apply(environment) + + require.NoError(t, err) + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Xmx1111M") +} + +func TestApplyJvmTraitHttpProxyArgs(t *testing.T) { + trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) + d := appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: defaultContainerName, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + Value: "http://my-user:my-password@my-proxy:1234", + }, + { + Name: "HTTPS_PROXY", + Value: "https://my-secure-user:my-secure-password@my-secure-proxy:6789", + }, + { + Name: "NO_PROXY", + Value: "https://my-non-proxied-host,1.2.3.4", + }, + }, + }, + }, + }, + }, + }, + } + environment.Resources.Add(&d) + err := trait.Apply(environment) + + require.NoError(t, err) + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Dhttp.proxyHost=\"my-proxy\"") + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Dhttp.proxyPort=\"1234\"") + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Dhttp.proxyUser=\"my-user\"") + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Dhttp.proxyPassword=\"my-password\"") + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Dhttps.proxyHost=\"my-secure-proxy\"") + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Dhttps.proxyPort=\"6789\"") + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Dhttps.proxyUser=\"my-secure-user\"") + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Dhttps.proxyPassword=\"my-secure-password\"") + assert.Contains(t, d.Spec.Template.Spec.Containers[0].Args, "-Dhttp.nonProxyHosts=\"https://my-non-proxied-host|1.2.3.4\"") +} func createNominalJvmTest(kitType string) (*jvmTrait, *Environment) { catalog, _ := camel.DefaultCatalog() @@ -326,3 +564,26 @@ func createNominalJvmTest(kitType string) (*jvmTrait, *Environment) { return trait, environment } + +func TestGetKitDependenciesDirectories(t *testing.T) { + kit := &v1.IntegrationKit{ + Status: v1.IntegrationKitStatus{ + Artifacts: []v1.Artifact{ + {Target: "my-dir1/lib/mytest.jar"}, + {Target: "my-dir1/lib/mytest2.jar"}, + {Target: "my-dir1/lib/mytest3.jar"}, + {Target: "my-dir2/lib/mytest4.jar"}, + {Target: "my-dir1/lib2/mytest5.jar"}, + {Target: "my-dir/mytest6.jar"}, + {Target: "my-dir/mytest7.jar"}, + }, + Phase: v1.IntegrationKitPhaseReady, + }, + } + paths := getKitDependenciesDirectories(kit) + assert.Len(t, paths, 4) + assert.Equal(t, "my-dir/*", paths[0]) + assert.Equal(t, "my-dir1/lib/*", paths[1]) + assert.Equal(t, "my-dir1/lib2/*", paths[2]) + assert.Equal(t, "my-dir2/lib/*", paths[3]) +}
