Skip to content

Commit

Permalink
feat(runtime): enable source less integrations
Browse files Browse the repository at this point in the history
Closes #3295
  • Loading branch information
squakez committed Oct 18, 2023
1 parent 77b0290 commit 07cf2ed
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 14 deletions.
26 changes: 26 additions & 0 deletions e2e/common/runtimes/default.go
Original file line number Diff line number Diff line change
@@ -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())
70 changes: 70 additions & 0 deletions e2e/common/runtimes/runtimes_test.go
Original file line number Diff line number Diff line change
@@ -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())
}
30 changes: 22 additions & 8 deletions pkg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down
34 changes: 28 additions & 6 deletions pkg/cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand Down Expand Up @@ -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)
}
2 changes: 2 additions & 0 deletions pkg/util/kubernetes/sanitize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions script/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down

0 comments on commit 07cf2ed

Please sign in to comment.