Repository: apache/camel-k (go)

Issue:
Can't determine knative endpoint address
It happens on 0.7.1 with both serving v1alpha1 and v1beta1 because location of endpoint changed in an incompatible way.

This problem led to release Knative of 0.7.1 right after 0.7.0.

Reference: https://github.com/knative/eventing-contrib/pull/478

Pull request:
Fix #856: lookup destination in multiple places and add e2e tests
Fix #856

Patch:
diff --git a/.gitignore b/.gitignore
index 2513a3296..35ad0f8bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,6 +126,7 @@ Session.vim
 tags
 
 ### VisualStudioCode ###
+.vscode
 .vscode/*
 !.vscode/settings.json
 !.vscode/tasks.json
diff --git a/docs/modules/ROOT/pages/running/dev-mode.adoc b/docs/modules/ROOT/pages/running/dev-mode.adoc
index 59577a005..af04da325 100644
--- a/docs/modules/ROOT/pages/running/dev-mode.adoc
+++ b/docs/modules/ROOT/pages/running/dev-mode.adoc
@@ -7,7 +7,7 @@ It's called *dev mode*.
 Differently from other frameworks, artifacts generated by Camel K in *dev mode are no different* from the one you run in production.
 Dev mode is just a helper to let you be quicker during development.
 
-Tu enable dev mode, just add the `--dev` flag when running the integration:
+To enable dev mode, just add the `--dev` flag when running the integration:
 
 ```
 kamel run examples/Sample.java --dev
diff --git a/pkg/apis/camel/v1alpha1/knative/types_support.go b/pkg/apis/camel/v1alpha1/knative/types_support.go
index 4c9e53eb0..f18f35b22 100644
--- a/pkg/apis/camel/v1alpha1/knative/types_support.go
+++ b/pkg/apis/camel/v1alpha1/knative/types_support.go
@@ -24,11 +24,7 @@ import (
 )
 
 // BuildCamelServiceDefinition creates a CamelServiceDefinition from a given URL
-func BuildCamelServiceDefinition(name string, serviceType CamelServiceType, rawurl string) (*CamelServiceDefinition, error) {
-	serviceURL, err := url.Parse(rawurl)
-	if err != nil {
-		return nil, err
-	}
+func BuildCamelServiceDefinition(name string, serviceType CamelServiceType, serviceURL url.URL) (CamelServiceDefinition, error) {
 	protocol := CamelProtocol(serviceURL.Scheme)
 	definition := CamelServiceDefinition{
 		Name:        name,
@@ -42,7 +38,7 @@ func BuildCamelServiceDefinition(name string, serviceType CamelServiceType, rawu
 	if portStr != "" {
 		port, err := strconv.Atoi(portStr)
 		if err != nil {
-			return nil, err
+			return CamelServiceDefinition{}, err
 		}
 		definition.Port = port
 	}
@@ -52,7 +48,7 @@ func BuildCamelServiceDefinition(name string, serviceType CamelServiceType, rawu
 	} else {
 		definition.Metadata[CamelMetaServicePath] = "/"
 	}
-	return &definition, nil
+	return definition, nil
 }
 
 func defaultCamelProtocolPort(prot CamelProtocol) int {
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index 9f13bb652..166b86285 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -73,6 +73,7 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	cmd.Flags().StringSliceVar(&impl.kits, "kit", nil, "Add an integration kit to build at startup")
 	cmd.Flags().StringVar(&impl.buildStrategy, "build-strategy", "", "Set the build strategy")
 	cmd.Flags().StringVar(&impl.buildTimeout, "build-timeout", "", "Set how long the build process can last")
+	cmd.Flags().StringVar(&impl.traitProfile, "trait-profile", "", "The profile to use for traits")
 
 	// maven settings
 	cmd.Flags().StringVar(&impl.localRepository, "local-repository", "", "Location of the local maven repository")
@@ -112,6 +113,7 @@ type installCmdOptions struct {
 	properties        []string
 	kits              []string
 	registry          v1alpha1.IntegrationPlatformRegistrySpec
+	traitProfile      string
 }
 
 // nolint: gocyclo
@@ -208,6 +210,9 @@ func (o *installCmdOptions) install(_ *cobra.Command, _ []string) error {
 
 			platform.Spec.Build.Timeout.Duration = d
 		}
+		if o.traitProfile != "" {
+			platform.Spec.Profile = v1alpha1.TraitProfileByName(o.traitProfile)
+		}
 
 		if len(o.mavenRepositories) > 0 {
 			for _, r := range o.mavenRepositories {
@@ -318,6 +323,14 @@ func (o *installCmdOptions) validate(_ *cobra.Command, _ []string) error {
 		result = multierr.Append(result, err)
 	}
 
+	if o.traitProfile != "" {
+		tp := v1alpha1.TraitProfileByName(o.traitProfile)
+		if tp == v1alpha1.TraitProfile("") {
+			err := fmt.Errorf("unknown trait profile %s", o.traitProfile)
+			result = multierr.Append(result, err)
+		}
+	}
+
 	return result
 }
 
diff --git a/pkg/trait/dependencies.go b/pkg/trait/dependencies.go
index 2fd1a56ff..453806c8d 100644
--- a/pkg/trait/dependencies.go
+++ b/pkg/trait/dependencies.go
@@ -50,7 +50,7 @@ func (t *dependenciesTrait) Apply(e *Environment) error {
 			util.StringSliceUniqueAdd(&dependencies, dep)
 		}
 	}
-	for _, s := range e.Integration.Spec.Sources {
+	for _, s := range e.Integration.Sources() {
 		meta := metadata.Extract(e.CamelCatalog, s)
 
 		switch s.InferLanguage() {
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index 698bee5f9..f1d84db32 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -19,18 +19,19 @@ package trait
 
 import (
 	"fmt"
+	"net/url"
 	"strings"
 
-	"github.com/pkg/errors"
-
-	"github.com/scylladb/go-set/strset"
-
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/metadata"
 	"github.com/apache/camel-k/pkg/util/envvar"
+	"github.com/pkg/errors"
+	"github.com/scylladb/go-set/strset"
 
 	knativeapi "github.com/apache/camel-k/pkg/apis/camel/v1alpha1/knative"
 	knativeutil "github.com/apache/camel-k/pkg/util/knative"
+	duck "github.com/knative/pkg/apis/duck/v1alpha1"
+	serving "github.com/knative/serving/pkg/apis/serving/v1alpha1"
 )
 
 type knativeTrait struct {
@@ -210,19 +211,13 @@ func (t *knativeTrait) configureChannels(e *Environment, env *knativeapi.CamelEn
 		if err != nil {
 			return err
 		}
-		if c == nil || c.Status.Address.Hostname == "" {
-			return errors.New("cannot find address of channel " + ch)
+		if c == nil {
+			return errors.Errorf("cannot find channel %s", ch)
 		}
 
-		svc := knativeapi.CamelServiceDefinition{
-			Name:        ch,
-			Host:        c.Status.Address.Hostname,
-			Port:        80,
-			Protocol:    knativeapi.CamelProtocolHTTP,
-			ServiceType: knativeapi.CamelServiceTypeChannel,
-			Metadata: map[string]string{
-				knativeapi.CamelMetaServicePath: "/",
-			},
+		svc, err := buildServiceDefinition(ch, knativeapi.CamelServiceTypeChannel, c.Status.Address)
+		if err != nil {
+			return errors.Wrapf(err, "cannot determine address of channel %s", ch)
 		}
 		env.Services = append(env.Services, svc)
 	}
@@ -270,19 +265,12 @@ func (t *knativeTrait) configureEndpoints(e *Environment, env *knativeapi.CamelE
 		if err != nil {
 			return err
 		}
-		if s == nil || s.Status.Address == nil || s.Status.Address.Hostname == "" {
-			return errors.New("cannot find address of endpoint " + endpoint)
+		if s == nil {
+			return errors.Errorf("cannot find endpoint %s", endpoint)
 		}
-
-		svc := knativeapi.CamelServiceDefinition{
-			Name:        endpoint,
-			Host:        s.Status.Address.Hostname,
-			Port:        80,
-			Protocol:    knativeapi.CamelProtocolHTTP,
-			ServiceType: knativeapi.CamelServiceTypeEndpoint,
-			Metadata: map[string]string{
-				knativeapi.CamelMetaServicePath: "/",
-			},
+		svc, err := buildServiceDefinitionFromStatus(endpoint, knativeapi.CamelServiceTypeEndpoint, s.Status)
+		if err != nil {
+			return errors.Wrapf(err, "cannot determine address of endpoint %s", endpoint)
 		}
 		env.Services = append(env.Services, svc)
 	}
@@ -301,3 +289,33 @@ func (t *knativeTrait) extractNames(names string) []string {
 
 	return answer
 }
+
+// buildServiceDefinitionFromStatus creates a CamelServiceDefinition from a Knative ServiceStatus
+func buildServiceDefinitionFromStatus(name string, serviceType knativeapi.CamelServiceType, status serving.ServiceStatus) (knativeapi.CamelServiceDefinition, error) {
+	// build it using the Route URL information if available
+	if status.URL != nil && status.URL.Host != "" {
+		return knativeapi.BuildCamelServiceDefinition(name, serviceType, url.URL(*status.URL))
+	}
+	// fallback to using the addressable
+	if status.Address != nil {
+		return buildServiceDefinition(name, serviceType, *status.Address)
+	}
+	return knativeapi.CamelServiceDefinition{}, errors.New("cannot determine service hostname")
+}
+
+// buildServiceDefinition creates a CamelServiceDefinition from a Knative Addressable
+func buildServiceDefinition(name string, serviceType knativeapi.CamelServiceType, addressable duck.Addressable) (knativeapi.CamelServiceDefinition, error) {
+	// build it using the URL information if available
+	if addressable.URL != nil && addressable.URL.Host != "" {
+		return knativeapi.BuildCamelServiceDefinition(name, serviceType, url.URL(*addressable.URL))
+	}
+	// fallback to using hostname
+	if addressable.Hostname == "" {
+		return knativeapi.CamelServiceDefinition{}, errors.New("cannot determine addressable hostname")
+	}
+	serviceURL, err := url.Parse(fmt.Sprintf("http://%s", addressable.Hostname))
+	if err != nil {
+		return knativeapi.CamelServiceDefinition{}, err
+	}
+	return knativeapi.BuildCamelServiceDefinition(name, serviceType, *serviceURL)
+}
diff --git a/pkg/util/knative/uri.go b/pkg/util/knative/uri.go
index 66caaa3f6..c2f50ed3b 100644
--- a/pkg/util/knative/uri.go
+++ b/pkg/util/knative/uri.go
@@ -23,7 +23,7 @@ import (
 	knativev1 "github.com/apache/camel-k/pkg/apis/camel/v1alpha1/knative"
 )
 
-var uriRegexp = regexp.MustCompile("^knative:[/]*(channel|endpoint)/([a-z0-9.-]+)(?:[/?].*|$)")
+var uriRegexp = regexp.MustCompile(`^knative:[/]*(channel|endpoint)/([a-z>-Z0-9.-]+)(?:[/?].*|$)`)
 
 // ExtractChannelNames extracts all Knative named channels from the given URIs
 func ExtractChannelNames(uris []string) []string {
diff --git a/script/Makefile b/script/Makefile
index 732e96c45..f13df4c3d 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -24,6 +24,8 @@ IMAGE_NAME := docker.io/apache/camel-k
 RELEASE_GIT_REMOTE := upstream
 GIT_COMMIT := $(shell git rev-list -1 HEAD)
 GO_PATH := $(shell go env GOPATH)
+LINT_GOGC := 10
+LINT_DEADLINE := 5m
 
 # For signing binary artifacts
 GPG_PASS := $(GPG_PASS)
@@ -104,6 +106,9 @@ test: build
 test-integration: build
 	go test -timeout 30m -v ./e2e/... -tags=integration
 
+test-knative: build
+	go test -timeout 30m -v ./e2e/... -tags=knative
+
 build-operator:
 	go build $(GOFLAGS) -o camel-k ./cmd/manager/*.go
 
@@ -122,7 +127,7 @@ build-olm:
 	./script/build_olm.sh $(VERSION)
 
 build-compile-integration-tests:
-	go test -c -tags=integration ./e2e/*.go
+	go test -c -tags="integration knative" ./e2e/*.go
 
 clean:
 	# go clean fails if modules support are turned on as it tries to 
@@ -150,7 +155,7 @@ dep:
 	go mod tidy
 
 lint:
-	golangci-lint run
+	GOGC=$(LINT_GOGC) golangci-lint run --deadline $(LINT_DEADLINE)
 
 images: test
 	mkdir -p build/_maven_output


Test patch:
diff --git a/e2e/files/knative1.groovy b/e2e/files/knative1.groovy
new file mode 100644
index 000000000..b69fc824e
--- /dev/null
+++ b/e2e/files/knative1.groovy
@@ -0,0 +1,20 @@
+/*
+ * 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('timer:tick')
+  .to('knative:endpoint/knative2')
+  .log('Received: ${body}')
diff --git a/e2e/files/knative2.groovy b/e2e/files/knative2.groovy
new file mode 100644
index 000000000..cb1c6fbda
--- /dev/null
+++ b/e2e/files/knative2.groovy
@@ -0,0 +1,19 @@
+/*
+ * 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('knative:endpoint/knative2')
+  .setBody().constant("Hello from knative2")
diff --git a/e2e/files/knativech1.groovy b/e2e/files/knativech1.groovy
new file mode 100644
index 000000000..1a32a6488
--- /dev/null
+++ b/e2e/files/knativech1.groovy
@@ -0,0 +1,20 @@
+/*
+ * 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('timer:tick')
+  .setBody().constant("Hello from knativech1")
+  .to('knative:channel/messages')
diff --git a/e2e/files/knativech2.groovy b/e2e/files/knativech2.groovy
new file mode 100644
index 000000000..65eeb2eb9
--- /dev/null
+++ b/e2e/files/knativech2.groovy
@@ -0,0 +1,19 @@
+/*
+ * 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('knative:channel/messages')
+  .log('Received: ${body}')
diff --git a/e2e/knative_test.go b/e2e/knative_test.go
new file mode 100644
index 000000000..371add2f9
--- /dev/null
+++ b/e2e/knative_test.go
@@ -0,0 +1,57 @@
+// +build knative
+
+// To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "knative"
+
+/*
+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 e2e
+
+import (
+	"testing"
+	"time"
+
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+)
+
+func TestRunServiceCombo(t *testing.T) {
+	withNewTestNamespace(func(ns string) {
+		RegisterTestingT(t)
+		Expect(kamel("install", "-n", ns, "--trait-profile", "knative").Execute()).Should(BeNil())
+		Expect(kamel("run", "-n", ns, "files/knative2.groovy").Execute()).Should(BeNil())
+		Expect(kamel("run", "-n", ns, "files/knative1.groovy").Execute()).Should(BeNil())
+		Eventually(integrationPodPhase(ns, "knative2"), 10*time.Minute).Should(Equal(v1.PodRunning))
+		Eventually(integrationPodPhase(ns, "knative1"), 10*time.Minute).Should(Equal(v1.PodRunning))
+		Eventually(integrationLogs(ns, "knative1"), 5*time.Minute).Should(ContainSubstring("Received: Hello from knative2"))
+		Expect(kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil())
+	})
+}
+
+func TestRunChannelCombo(t *testing.T) {
+	withNewTestNamespace(func(ns string) {
+		RegisterTestingT(t)
+		Expect(createKnativeChannel(ns, "messages")()).Should(BeNil())
+		Expect(kamel("install", "-n", ns, "--trait-profile", "knative").Execute()).Should(BeNil())
+		Expect(kamel("run", "-n", ns, "files/knativech2.groovy").Execute()).Should(BeNil())
+		Expect(kamel("run", "-n", ns, "files/knativech1.groovy").Execute()).Should(BeNil())
+		Eventually(integrationPodPhase(ns, "knativech2"), 10*time.Minute).Should(Equal(v1.PodRunning))
+		Eventually(integrationPodPhase(ns, "knativech1"), 10*time.Minute).Should(Equal(v1.PodRunning))
+		Eventually(integrationLogs(ns, "knativech2"), 5*time.Minute).Should(ContainSubstring("Received: Hello from knativech1"))
+		Expect(kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil())
+	})
+}
diff --git a/e2e/test_support.go b/e2e/test_support.go
index 134c825fa..5a0ca4050 100644
--- a/e2e/test_support.go
+++ b/e2e/test_support.go
@@ -1,4 +1,4 @@
-// +build integration
+// +build integration knative
 
 // To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "integration"
 
@@ -35,6 +35,7 @@ import (
 	"github.com/apache/camel-k/pkg/util/log"
 	"github.com/apache/camel-k/pkg/util/openshift"
 	"github.com/google/uuid"
+	eventing "github.com/knative/eventing/pkg/apis/eventing/v1alpha1"
 	"github.com/onsi/gomega"
 	projectv1 "github.com/openshift/api/project/v1"
 	"github.com/spf13/cobra"
@@ -408,6 +409,34 @@ func scaleOperator(ns string, replicas int32) func() error {
 	}
 }
 
+/*
+	Knative
+ */
+
+func createKnativeChannel(ns string, name string) func() error {
+	return func() error {
+		channel := eventing.Channel{
+			TypeMeta: metav1.TypeMeta{
+				Kind: "Channel",
+				APIVersion: eventing.SchemeGroupVersion.String(),
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: ns,
+				Name: name,
+			},
+			Spec: eventing.ChannelSpec{
+				Provisioner: &v1.ObjectReference{
+					APIVersion: "eventing.knative.dev/v1alpha1",
+					Kind: "ClusterChannelProvisioner",
+					Name: "in-memory",
+				},
+			},
+		}
+		return testClient.Create(testContext, &channel)
+	}
+}
+
+
 /*
 	Namespace testing functions
 */
diff --git a/pkg/trait/dependencies_test.go b/pkg/trait/dependencies_test.go
index 3744264d6..e795fa803 100644
--- a/pkg/trait/dependencies_test.go
+++ b/pkg/trait/dependencies_test.go
@@ -125,3 +125,51 @@ func TestIntegrationCustomDeps(t *testing.T) {
 		e.Integration.Status.Dependencies,
 	)
 }
+
+func TestIntegrationAutoGeneratedDeps(t *testing.T) {
+	catalog, err := test.DefaultCatalog()
+	assert.Nil(t, err)
+
+	e := &Environment{
+		CamelCatalog: catalog,
+		Integration: &v1alpha1.Integration{
+			Spec: v1alpha1.IntegrationSpec{
+				Sources: []v1alpha1.SourceSpec{
+					{
+						DataSpec: v1alpha1.DataSpec{
+							Name:    "Request.java",
+							Content: `from("direct:foo").to("log:bar");`,
+						},
+						Language: v1alpha1.LanguageJavaSource,
+					},
+				},
+			},
+			Status: v1alpha1.IntegrationStatus{
+				Phase: v1alpha1.IntegrationPhaseInitialization,
+				GeneratedSources: []v1alpha1.SourceSpec{
+					{
+						DataSpec: v1alpha1.DataSpec{
+							Name:    "RequestAuto.xml",
+							Content: `<rests xmlns="http://camel.apache.org/schema/spring"><rest path="/camel/"></rest></rests>`,
+						},
+						Language: v1alpha1.LanguageXML,
+					},
+				},
+			},
+		},
+	}
+
+	trait := newDependenciesTrait()
+	enabled, err := trait.Configure(e)
+	assert.Nil(t, err)
+	assert.True(t, enabled)
+
+	err = trait.Apply(e)
+	assert.Nil(t, err)
+	assert.ElementsMatch(
+		t,
+		[]string{"camel:direct", "camel:log", "mvn:org.apache.camel.k/camel-k-loader-java", "mvn:org.apache.camel.k/camel-k-loader-xml",
+			"mvn:org.apache.camel.k/camel-k-runtime-main"},
+		e.Integration.Status.Dependencies,
+	)
+}
diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go
index 1216f0b11..82e35009a 100644
--- a/pkg/trait/knative_test.go
+++ b/pkg/trait/knative_test.go
@@ -21,22 +21,15 @@ import (
 	"context"
 	"testing"
 
-	"github.com/apache/camel-k/pkg/util/envvar"
-
-	"k8s.io/apimachinery/pkg/runtime"
-
-	"github.com/apache/camel-k/pkg/apis"
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1/knative"
+	"github.com/apache/camel-k/pkg/client"
+	"github.com/apache/camel-k/pkg/util/envvar"
 	"github.com/apache/camel-k/pkg/util/test"
 
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-
 	"github.com/scylladb/go-set/strset"
-
 	"github.com/stretchr/testify/assert"
 
-	"k8s.io/client-go/kubernetes"
-
 	knativeapi "github.com/apache/camel-k/pkg/apis/camel/v1alpha1/knative"
 	k8sutils "github.com/apache/camel-k/pkg/util/kubernetes"
 	eventing "github.com/knative/eventing/pkg/apis/eventing/v1alpha1"
@@ -44,11 +37,9 @@ import (
 	serving "github.com/knative/serving/pkg/apis/serving/v1alpha1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	clientscheme "k8s.io/client-go/kubernetes/scheme"
-	controller "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-func TestKnativeEnvConfiguration(t *testing.T) {
+func TestKnativeEnvConfigurationFromTrait(t *testing.T) {
 	catalog, err := test.DefaultCatalog()
 	assert.Nil(t, err)
 
@@ -107,9 +98,7 @@ func TestKnativeEnvConfiguration(t *testing.T) {
 	c, err := NewFakeClient("ns")
 	assert.Nil(t, err)
 
-	client := &FakeClient{Client: c, Interface: nil}
-
-	tc := NewCatalog(context.TODO(), client)
+	tc := NewCatalog(context.TODO(), c)
 
 	err = tc.configure(&environment)
 	assert.Nil(t, err)
@@ -148,28 +137,107 @@ func TestKnativeEnvConfiguration(t *testing.T) {
 	eSink2 := ne.FindService("endpoint-sink-2", knativeapi.CamelServiceTypeEndpoint)
 	assert.NotNil(t, eSink2)
 	assert.Equal(t, "endpoint-sink-2.host", eSink2.Host)
-
-}
-
-type FakeClient struct {
-	controller.Client
-	kubernetes.Interface
 }
 
-func (c *FakeClient) GetScheme() *runtime.Scheme {
-	return nil
-}
+func TestKnativeEnvConfigurationFromSource(t *testing.T) {
+	catalog, err := test.DefaultCatalog()
+	assert.Nil(t, err)
 
-func NewFakeClient(namespace string) (controller.Client, error) {
-	scheme := clientscheme.Scheme
+	traitCatalog := NewCatalog(context.TODO(), nil)
 
-	// Setup Scheme for all resources
-	if err := apis.AddToScheme(scheme); err != nil {
-		return nil, err
+	environment := Environment{
+		CamelCatalog: catalog,
+		Catalog:      traitCatalog,
+		Integration: &v1alpha1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test",
+				Namespace: "ns",
+			},
+			Status: v1alpha1.IntegrationStatus{
+				Phase: v1alpha1.IntegrationPhaseDeploying,
+			},
+			Spec: v1alpha1.IntegrationSpec{
+				Profile: v1alpha1.TraitProfileKnative,
+				Sources: []v1alpha1.SourceSpec{
+					{
+						DataSpec: v1alpha1.DataSpec{
+							Name: "route.java",
+							Content: `
+								public class CartoonMessagesMover extends RouteBuilder {
+									public void configure() {
+										from("knative:endpoint/s3fileMover1")
+											.log("${body}");
+									}
+								}
+							`,
+						},
+						Language: v1alpha1.LanguageJavaSource,
+					},
+				},
+				Resources: []v1alpha1.ResourceSpec{},
+				Traits: map[string]v1alpha1.TraitSpec{
+					"knative": {
+						Configuration: map[string]string{
+							"enabled": "true",
+						},
+					},
+				},
+			},
+		},
+		IntegrationKit: &v1alpha1.IntegrationKit{
+			Status: v1alpha1.IntegrationKitStatus{
+				Phase: v1alpha1.IntegrationKitPhaseReady,
+			},
+		},
+		Platform: &v1alpha1.IntegrationPlatform{
+			Spec: v1alpha1.IntegrationPlatformSpec{
+				Cluster: v1alpha1.IntegrationPlatformClusterOpenShift,
+				Build: v1alpha1.IntegrationPlatformBuildSpec{
+					PublishStrategy: v1alpha1.IntegrationPlatformBuildPublishStrategyS2I,
+					Registry:        v1alpha1.IntegrationPlatformRegistrySpec{Address: "registry"},
+				},
+				Profile: v1alpha1.TraitProfileKnative,
+			},
+		},
+		EnvVars:        make([]corev1.EnvVar, 0),
+		ExecutedTraits: make([]Trait, 0),
+		Resources:      k8sutils.NewCollection(),
+		Classpath:      strset.New(),
 	}
 
-	return fake.NewFakeClientWithScheme(
-		scheme,
+	c, err := NewFakeClient("ns")
+	assert.Nil(t, err)
+
+	tc := NewCatalog(context.TODO(), c)
+
+	err = tc.configure(&environment)
+	assert.Nil(t, err)
+
+	tr := tc.GetTrait("knative").(*knativeTrait)
+
+	ok, err := tr.Configure(&environment)
+	assert.Nil(t, err)
+	assert.True(t, ok)
+
+	err = tr.Apply(&environment)
+	assert.Nil(t, err)
+
+	kc := envvar.Get(environment.EnvVars, "CAMEL_KNATIVE_CONFIGURATION")
+	assert.NotNil(t, kc)
+
+	ne := knativeapi.NewCamelEnvironment()
+	err = ne.Deserialize(kc.Value)
+	assert.Nil(t, err)
+
+	source := ne.FindService("s3fileMover1", knativeapi.CamelServiceTypeEndpoint)
+	assert.NotNil(t, source)
+	assert.Equal(t, knative.CamelProtocolHTTP, source.Protocol)
+	assert.Equal(t, "0.0.0.0", source.Host)
+	assert.Equal(t, 8080, source.Port)
+}
+
+func NewFakeClient(namespace string) (client.Client, error) {
+	return test.NewFakeClient(
 		&eventing.Channel{
 			TypeMeta: metav1.TypeMeta{
 				Kind:       "Channel",
@@ -219,5 +287,5 @@ func NewFakeClient(namespace string) (controller.Client, error) {
 				},
 			},
 		},
-	), nil
+	)
 }