You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2023/10/25 13:33:58 UTC
[camel-k] 01/02: feat(runtime): enable source less integrations
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 27e07a2fdde207099c2f7ce96b8ca0265aa07a27
Author: Pasquale Congiusti <pa...@gmail.com>
AuthorDate: Tue Oct 17 17:28:22 2023 +0200
feat(runtime): enable source less integrations
Closes #3295
---
e2e/common/runtimes/default.go | 26 ++++++++++++++
e2e/common/runtimes/runtimes_test.go | 70 ++++++++++++++++++++++++++++++++++++
pkg/cmd/run.go | 30 +++++++++++-----
pkg/cmd/run_test.go | 34 ++++++++++++++----
pkg/util/kubernetes/sanitize_test.go | 2 ++
script/Makefile | 1 +
6 files changed, 149 insertions(+), 14 deletions(-)
diff --git a/e2e/common/runtimes/default.go b/e2e/common/runtimes/default.go
new file mode 100644
index 000000000..1cc41c3f9
--- /dev/null
+++ b/e2e/common/runtimes/default.go
@@ -0,0 +1,26 @@
+//go:build integration
+// +build integration
+
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package runtimes
+
+import "github.com/apache/camel-k/v2/e2e/support"
+
+var ns = support.GetEnvOrDefault("CAMEL_K_TEST_NAMESPACE", support.GetCIProcessID())
+var operatorID = support.GetEnvOrDefault("CAMEL_K_OPERATOR_ID", support.GetCIProcessID())
diff --git a/e2e/common/runtimes/runtimes_test.go b/e2e/common/runtimes/runtimes_test.go
new file mode 100644
index 000000000..d95c6820c
--- /dev/null
+++ b/e2e/common/runtimes/runtimes_test.go
@@ -0,0 +1,70 @@
+//go:build integration
+// +build integration
+
+// To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "integration"
+
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package runtimes
+
+import (
+ "testing"
+
+ . "github.com/onsi/gomega"
+
+ corev1 "k8s.io/api/core/v1"
+
+ . "github.com/apache/camel-k/v2/e2e/support"
+ v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+)
+
+func TestSourceLessIntegrations(t *testing.T) {
+ RegisterTestingT(t)
+ var cmData = make(map[string]string)
+ cmData["my-file.txt"] = "Hello World!"
+ CreatePlainTextConfigmap(ns, "my-cm-sourceless", cmData)
+
+ t.Run("Camel Main", func(t *testing.T) {
+ itName := "my-camel-main-v1"
+ Expect(KamelRunWithID(operatorID, ns, "--image", "docker.io/squakez/my-camel-main:1.0.0", "--resource", "configmap:my-cm-sourceless@/tmp/app/data").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, itName), TestTimeoutShort).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns, itName, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, itName), TestTimeoutShort).Should(ContainSubstring(cmData["my-file.txt"]))
+ Eventually(IntegrationLogs(ns, itName), TestTimeoutShort).Should(ContainSubstring("Apache Camel (Main)"))
+ })
+
+ t.Run("Camel Spring Boot", func(t *testing.T) {
+ itName := "my-camel-sb-v1"
+ Expect(KamelRunWithID(operatorID, ns, "--image", "docker.io/squakez/my-camel-sb:1.0.0", "--resource", "configmap:my-cm-sourceless@/tmp/app/data").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, itName), TestTimeoutShort).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns, itName, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, itName), TestTimeoutShort).Should(ContainSubstring(cmData["my-file.txt"]))
+ Eventually(IntegrationLogs(ns, itName), TestTimeoutShort).Should(ContainSubstring("Spring Boot"))
+ })
+
+ t.Run("Camel Quarkus", func(t *testing.T) {
+ itName := "my-camel-quarkus-v1"
+ Expect(KamelRunWithID(operatorID, ns, "--image", "docker.io/squakez/my-camel-quarkus:1.0.0", "--resource", "configmap:my-cm-sourceless@/tmp/app/data").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, itName), TestTimeoutShort).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns, itName, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, itName), TestTimeoutShort).Should(ContainSubstring(cmData["my-file.txt"]))
+ Eventually(IntegrationLogs(ns, itName), TestTimeoutShort).Should(ContainSubstring("powered by Quarkus"))
+ })
+
+ Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
+}
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index f117f8138..ea8ba4710 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -98,6 +98,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions)
}
cmd.Flags().String("name", "", "The integration name")
+ cmd.Flags().String("image", "", "An image built externally (ie, via CICD). Enabling it will skip the Integration build phase.")
cmd.Flags().StringArrayP("connect", "c", nil, "A Service that the integration should bind to, specified as [[apigroup/]version:]kind:[namespace/]name")
cmd.Flags().StringArrayP("dependency", "d", nil, usageDependency)
cmd.Flags().BoolP("wait", "w", false, "Wait for the integration to be running")
@@ -145,6 +146,7 @@ type runCmdOptions struct {
Save bool `mapstructure:"save" yaml:",omitempty" kamel:"omitsave"`
IntegrationKit string `mapstructure:"kit" yaml:",omitempty"`
IntegrationName string `mapstructure:"name" yaml:",omitempty"`
+ ContainerImage string `mapstructure:"image" yaml:",omitempty"`
Profile string `mapstructure:"profile" yaml:",omitempty"`
OperatorID string `mapstructure:"operator-id" yaml:",omitempty"`
OutputFormat string `mapstructure:"output" yaml:",omitempty"`
@@ -236,10 +238,6 @@ func (o *runCmdOptions) decode(cmd *cobra.Command, args []string) error {
}
func (o *runCmdOptions) validateArgs(cmd *cobra.Command, args []string) error {
- if len(args) < 1 {
- return errors.New("run expects at least 1 argument, received 0")
- }
-
if _, err := source.Resolve(context.Background(), args, false, cmd); err != nil {
return fmt.Errorf("one of the provided sources is not reachable: %w", err)
}
@@ -324,6 +322,11 @@ func (o *runCmdOptions) run(cmd *cobra.Command, args []string) error {
}
}
+ // We need to make this check at this point, in order to have sources filled during decoding
+ if len(args) < 1 && o.ContainerImage == "" {
+ return errors.New("run command expects either an Integration source or the container image (via --image argument)")
+ }
+
integration, err := o.createOrUpdateIntegration(cmd, c, args)
if err != nil {
return err
@@ -536,8 +539,15 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd *cobra.Command, c client.C
return nil, err
}
- if err := o.resolveSources(cmd, sources, integration); err != nil {
- return nil, err
+ if o.ContainerImage == "" {
+ // Resolve resources
+ if err := o.resolveSources(cmd, sources, integration); err != nil {
+ return nil, err
+ }
+ } else {
+ // Source-less Integration as the user provided a container image built externally
+ o.Traits = append(o.Traits, fmt.Sprintf("container.image=%s", o.ContainerImage))
+ o.Traits = append(o.Traits, "jvm.enabled=false")
}
if err := resolvePodTemplate(context.Background(), cmd, o.PodTemplate, &integration.Spec); err != nil {
@@ -868,11 +878,15 @@ func (o *runCmdOptions) getPlatform(cmd *cobra.Command, c client.Client, it *v1.
func (o *runCmdOptions) GetIntegrationName(sources []string) string {
name := ""
- if o.IntegrationName != "" {
+ switch {
+ case o.IntegrationName != "":
name = o.IntegrationName
name = kubernetes.SanitizeName(name)
- } else if len(sources) == 1 {
+ case len(sources) == 1:
name = kubernetes.SanitizeName(sources[0])
+ case o.ContainerImage != "":
+ // source-less execution
+ name = kubernetes.SanitizeName(strings.ReplaceAll(o.ContainerImage, ":", "-v"))
}
return name
}
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index becac009b..6ca17a838 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -511,13 +511,9 @@ func TestRunBuildPropertyFlag(t *testing.T) {
func TestRunValidateArgs(t *testing.T) {
runCmdOptions, rootCmd, _ := initializeRunCmdOptions(t)
- args := []string{}
- err := runCmdOptions.validateArgs(rootCmd, args)
- assert.NotNil(t, err)
- assert.Equal(t, "run expects at least 1 argument, received 0", err.Error())
- args = []string{"run_test.go"}
- err = runCmdOptions.validateArgs(rootCmd, args)
+ args := []string{"run_test.go"}
+ err := runCmdOptions.validateArgs(rootCmd, args)
assert.Nil(t, err)
args = []string{"missing_file"}
@@ -826,3 +822,29 @@ func TestRunOutputWithoutKubernetesCluster(t *testing.T) {
_, err = test.ExecuteCommand(rootCmd, cmdRun, "-o", "yaml", integrationSource)
require.NoError(t, err)
}
+
+func TestSourceLessIntegration(t *testing.T) {
+ runCmdOptions, runCmd, _ := initializeRunCmdOptionsWithOutput(t)
+ output, err := test.ExecuteCommand(runCmd, cmdRun, "--image", "docker.io/my-org/my-app:1.0.0", "-o", "yaml", "-t", "mount.configs=configmap:my-cm")
+ assert.Equal(t, "yaml", runCmdOptions.OutputFormat)
+
+ assert.Nil(t, err)
+ assert.Equal(t, `apiVersion: camel.apache.org/v1
+kind: Integration
+metadata:
+ annotations:
+ camel.apache.org/operator.id: camel-k
+ creationTimestamp: null
+ name: my-app-v1
+spec:
+ traits:
+ container:
+ image: docker.io/my-org/my-app:1.0.0
+ jvm:
+ enabled: false
+ mount:
+ configs:
+ - configmap:my-cm
+status: {}
+`, output)
+}
diff --git a/pkg/util/kubernetes/sanitize_test.go b/pkg/util/kubernetes/sanitize_test.go
index 31b422e20..93bcac937 100644
--- a/pkg/util/kubernetes/sanitize_test.go
+++ b/pkg/util/kubernetes/sanitize_test.go
@@ -36,6 +36,8 @@ func TestSanitizeName(t *testing.T) {
{"input": "-foo-bar-", "expect": "foo-bar"},
{"input": "1foo-bar2", "expect": "1foo-bar2"},
{"input": "foo-bar-1", "expect": "foo-bar-1"},
+ {"input": "docker.io/squakez/my-camel-sb:1.0.0", "expect": "my-camel-sb1"},
+ {"input": "docker.io/squakez/my-camel-sb:2.0.0", "expect": "my-camel-sb2"},
}
for _, c := range cases {
diff --git a/script/Makefile b/script/Makefile
index ac59c1692..786ced9ca 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -260,6 +260,7 @@ test-common: do-build
go test -timeout 30m -v ./e2e/common/config -tags=integration $(TEST_INTEGRATION_COMMON_LANG_RUN) $(GOTESTFMT) || FAILED=1; \
go test -timeout 30m -v ./e2e/common/misc -tags=integration $(TEST_INTEGRATION_COMMON_LANG_RUN) $(GOTESTFMT) || FAILED=1; \
go test -timeout 60m -v ./e2e/common/traits -tags=integration $(TEST_INTEGRATION_COMMON_LANG_RUN) $(GOTESTFMT) || FAILED=1; \
+ go test -timeout 20m -v ./e2e/common/runtimes -tags=integration $(TEST_INTEGRATION_COMMON_LANG_RUN) $(GOTESTFMT) || FAILED=1; \
go test -timeout 10m -v ./e2e/common/support/teardown_test.go -tags=integration $(TEST_INTEGRATION_COMMON_LANG_RUN) $(GOTESTFMT) || FAILED=1; \
exit $${FAILED}