Repository: apache/camel-k
Pull request #4243 (2023-04-11 23:11:00)

Improve error reporting in case of knative is required but not installed (1.10 branch)
Fix https://github.com/apache/camel-k/issues/3803

(cherry picked from commit e1e74fc327fdcfc039c0926db9b9699d51929c89)

**Release Note**
```release-note
Fix: Improve error reporting in case of knative is required but not installed
```



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f0c65fd62..8ee1d84b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,13 +6,21 @@
 
 **Closed issues:**
 
+- unable to access the URL and unable to expose port after the integration build is successfully deployed. [\#4256](https://github.com/apache/camel-k/issues/4256)
+- unable to execute "kamel run" in secure mode [\#4249](https://github.com/apache/camel-k/issues/4249)
+- Builder pod that hit timeout is not terminated [\#4241](https://github.com/apache/camel-k/issues/4241)
+- Introduce Code Coverage badge [\#4227](https://github.com/apache/camel-k/issues/4227)
+- CamelCatalog reconciliation fail [\#4226](https://github.com/apache/camel-k/issues/4226)
 - duplicate routeId issue [\#4225](https://github.com/apache/camel-k/issues/4225)
 - `KameletBinding` should own the related `Integration` [\#4219](https://github.com/apache/camel-k/issues/4219)
 - `kamel rebuild` does not rebuild integrations correctly [\#4218](https://github.com/apache/camel-k/issues/4218)
 - Local file in unit test leftover [\#4217](https://github.com/apache/camel-k/issues/4217)
 - Validate error [\#4214](https://github.com/apache/camel-k/issues/4214)
 - `kamel rebuild` does not rebuild KameletBindings [\#4208](https://github.com/apache/camel-k/issues/4208)
+- Allow E2E suite test to run against a remote cluster [\#4207](https://github.com/apache/camel-k/issues/4207)
+- Cannot copy local runtime dependencies [\#4198](https://github.com/apache/camel-k/issues/4198)
 - Creation of monitoring resources fail in a new GKE cluster [\#4191](https://github.com/apache/camel-k/issues/4191)
+- Unable to produce and consume in camel-k to apache kafka, getting error like "Connection to node -1 \(localhost/127.0.0.1:9092\) could not be established. Broker may not be available." [\#4186](https://github.com/apache/camel-k/issues/4186)
 - Remove additional custom labels utility [\#4183](https://github.com/apache/camel-k/issues/4183)
 - Makefile not working on Mac [\#4175](https://github.com/apache/camel-k/issues/4175)
 - Kamelet development using Camel DSL instead of YAML DSL [\#4174](https://github.com/apache/camel-k/issues/4174)
@@ -24,7 +32,11 @@
 - Kamel run sample integration is failing to run [\#4150](https://github.com/apache/camel-k/issues/4150)
 - Bring an option to force an image build to the kamel run command [\#4126](https://github.com/apache/camel-k/issues/4126)
 - How to use JVM classpaths in Native mode? [\#4124](https://github.com/apache/camel-k/issues/4124)
+- Expose runtime details in `kamel version -a -v` [\#3836](https://github.com/apache/camel-k/issues/3836)
+- Remove KEP cryptic message when installing [\#3829](https://github.com/apache/camel-k/issues/3829)
+- Improve error reporting in case of knative is required but not installed [\#3803](https://github.com/apache/camel-k/issues/3803)
 - Private Registry With TLS  Can Not Work [\#3672](https://github.com/apache/camel-k/issues/3672)
+- Wrong KameletBinding definition leads to operator panic [\#3586](https://github.com/apache/camel-k/issues/3586)
 - Move from release nightly to Continuous Delivery [\#3260](https://github.com/apache/camel-k/issues/3260)
 - OLM basic installation integration test [\#3180](https://github.com/apache/camel-k/issues/3180)
 - Default to using Micrometer for integration monitoring [\#1773](https://github.com/apache/camel-k/issues/1773)
diff --git a/pkg/apis/camel/v1/integration_types.go b/pkg/apis/camel/v1/integration_types.go
index 3ba0c2768..372be19b1 100644
--- a/pkg/apis/camel/v1/integration_types.go
+++ b/pkg/apis/camel/v1/integration_types.go
@@ -171,6 +171,8 @@ const (
 	IntegrationConditionServiceAvailable IntegrationConditionType = "ServiceAvailable"
 	// IntegrationConditionKnativeServiceAvailable --
 	IntegrationConditionKnativeServiceAvailable IntegrationConditionType = "KnativeServiceAvailable"
+	// IntegrationConditionKnativeAvailable --
+	IntegrationConditionKnativeAvailable IntegrationConditionType = "KnativeAvailable"
 	// IntegrationConditionCronJobAvailable --
 	IntegrationConditionCronJobAvailable IntegrationConditionType = "CronJobAvailable"
 	// IntegrationConditionExposureAvailable --
@@ -210,6 +212,8 @@ const (
 	IntegrationConditionKnativeServiceAvailableReason string = "KnativeServiceAvailable"
 	// IntegrationConditionKnativeServiceNotAvailableReason --
 	IntegrationConditionKnativeServiceNotAvailableReason string = "KnativeServiceNotAvailable"
+	// IntegrationConditionKnativeNotInstalledReason --
+	IntegrationConditionKnativeNotInstalledReason string = "KnativeNotInstalled"
 	// IntegrationConditionCronJobAvailableReason --
 	IntegrationConditionCronJobAvailableReason string = "CronJobAvailableReason"
 	// IntegrationConditionCronJobNotAvailableReason --
diff --git a/pkg/trait/container.go b/pkg/trait/container.go
index 426d63efb..114b4d4b2 100644
--- a/pkg/trait/container.go
+++ b/pkg/trait/container.go
@@ -18,6 +18,7 @@ limitations under the License.
 package trait
 
 import (
+	"errors"
 	"fmt"
 	"path"
 
@@ -37,6 +38,7 @@ import (
 	"github.com/apache/camel-k/pkg/util/defaults"
 	"github.com/apache/camel-k/pkg/util/digest"
 	"github.com/apache/camel-k/pkg/util/envvar"
+	"github.com/apache/camel-k/pkg/util/knative"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
@@ -77,6 +79,27 @@ func (t *containerTrait) Configure(e *Environment) (bool, error) {
 		return false, nil
 	}
 
+	knativeInstalled, _ := knative.IsInstalled(e.Ctx, e.Client)
+	if e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !knativeInstalled {
+		hasKnativeEndpoint, err := containsEndpoint("knative", e, t.Client)
+		if err != nil {
+			return false, err
+		}
+
+		if hasKnativeEndpoint {
+			// fail fast the integration as there is no knative installed in the cluster
+			t.L.ForIntegration(e.Integration).Infof("Integration %s/%s contains knative endpoint that cannot run, as knative is not installed in the cluster.", e.Integration.Namespace, e.Integration.Name)
+			err := errors.New("integration cannot run, as knative is not installed in the cluster")
+			e.Integration.Status.SetCondition(
+				v1.IntegrationConditionKnativeAvailable,
+				corev1.ConditionFalse,
+				v1.IntegrationConditionKnativeNotInstalledReason,
+				err.Error())
+			e.Integration.Status.Phase = v1.IntegrationPhaseError
+			return false, err
+		}
+	}
+
 	if pointer.BoolDeref(t.Auto, true) {
 		if t.Expose == nil {
 			e := e.Resources.GetServiceForIntegration(e.Integration) != nil
diff --git a/pkg/trait/util.go b/pkg/trait/util.go
index 880098c71..f728d1dd5 100644
--- a/pkg/trait/util.go
+++ b/pkg/trait/util.go
@@ -38,7 +38,9 @@ import (
 	"github.com/apache/camel-k/pkg/metadata"
 	"github.com/apache/camel-k/pkg/util"
 	"github.com/apache/camel-k/pkg/util/camel"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/util/property"
+	"github.com/apache/camel-k/pkg/util/uri"
 )
 
 type Options map[string]map[string]interface{}
@@ -542,3 +544,25 @@ func FromAnnotations(meta *metav1.ObjectMeta) (Options, error) {
 
 	return options, nil
 }
+
+// verify if the integration in the Environment contains an endpoint.
+func containsEndpoint(name string, e *Environment, c client.Client) (bool, error) {
+	sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, c, e.Integration, e.Resources)
+	if err != nil {
+		return false, err
+	}
+
+	meta := metadata.ExtractAll(e.CamelCatalog, sources)
+
+	hasKnativeEndpoint := false
+	endpoints := make([]string, 0)
+	endpoints = append(endpoints, meta.FromURIs...)
+	endpoints = append(endpoints, meta.ToURIs...)
+	for _, endpoint := range endpoints {
+		if uri.GetComponent(endpoint) == name {
+			hasKnativeEndpoint = true
+			break
+		}
+	}
+	return hasKnativeEndpoint, nil
+}


Test patch:
diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
index ad7f8e861..245adbb38 100644
--- a/pkg/trait/builder_test.go
+++ b/pkg/trait/builder_test.go
@@ -31,6 +31,7 @@ import (
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/defaults"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
 func TestBuilderTraitNotAppliedBecauseOfNilKit(t *testing.T) {
@@ -108,10 +109,12 @@ func createBuilderTestEnv(cluster v1.IntegrationPlatformCluster, strategy v1.Int
 	}
 
 	kanikoCache := false
+	client, _ := test.NewFakeClient()
 	res := &Environment{
 		Ctx:          context.TODO(),
 		CamelCatalog: c,
 		Catalog:      NewCatalog(nil),
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "test",
diff --git a/pkg/trait/container_probes_test.go b/pkg/trait/container_probes_test.go
index 2642f032d..ac0bef1a2 100644
--- a/pkg/trait/container_probes_test.go
+++ b/pkg/trait/container_probes_test.go
@@ -29,6 +29,7 @@ import (
 	traitv1 "github.com/apache/camel-k/pkg/apis/camel/v1/trait"
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
 func newTestProbesEnv(t *testing.T, integration *v1.Integration) Environment {
@@ -38,11 +39,13 @@ func newTestProbesEnv(t *testing.T, integration *v1.Integration) Environment {
 	assert.Nil(t, err)
 	assert.NotNil(t, catalog)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	return Environment{
 		Catalog:               traitCatalog,
 		CamelCatalog:          catalog,
+		Client:                client,
 		Platform:              &v1.IntegrationPlatform{},
 		Integration:           integration,
 		Resources:             kubernetes.NewCollection(),
diff --git a/pkg/trait/container_test.go b/pkg/trait/container_test.go
index 54d2157b5..622693d84 100644
--- a/pkg/trait/container_test.go
+++ b/pkg/trait/container_test.go
@@ -27,6 +27,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/utils/pointer"
 
 	ctrl "sigs.k8s.io/controller-runtime/pkg/client"
 
@@ -41,11 +42,13 @@ func TestContainerWithDefaults(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      ServiceTestName,
@@ -96,11 +99,13 @@ func TestContainerWithCustomName(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      ServiceTestName,
@@ -314,3 +319,96 @@ func TestContainerWithImagePullPolicy(t *testing.T) {
 
 	assert.Equal(t, corev1.PullAlways, container.ImagePullPolicy)
 }
+
+func TestRunKnativeEndpointWithKnativeNotInstalled(t *testing.T) {
+
+	environment := createEnvironment()
+
+	trait, _ := newContainerTrait().(*containerTrait)
+	trait.Enabled = pointer.Bool(true)
+
+	environment.Integration.Spec.Sources = []v1.SourceSpec{
+		{
+			DataSpec: v1.DataSpec{
+				Name: "test.java",
+				Content: `
+				from("knative:channel/test").to("log:${body};
+			`,
+			},
+			Language: v1.LanguageJavaSource,
+		},
+	}
+	configured, err := trait.Configure(environment)
+	assert.NotNil(t, err)
+	assert.False(t, configured)
+	conditions := environment.Integration.Status.Conditions
+	assert.Len(t, conditions, 1)
+	assert.Equal(t, v1.IntegrationConditionKnativeNotInstalledReason, conditions[0].Reason)
+}
+
+func TestRunNonKnativeEndpointWithKnativeNotInstalled(t *testing.T) {
+
+	environment := createEnvironment()
+	trait, _ := newContainerTrait().(*containerTrait)
+	trait.Enabled = pointer.Bool(true)
+	environment.Integration.Spec.Sources = []v1.SourceSpec{
+		{
+			DataSpec: v1.DataSpec{
+				Name: "test.java",
+				Content: `
+				from("platform-http://my-site").to("log:${body}");
+			`,
+			},
+			Language: v1.LanguageJavaSource,
+		},
+	}
+
+	configured, err := trait.Configure(environment)
+	assert.Nil(t, err)
+	assert.True(t, configured)
+	conditions := environment.Integration.Status.Conditions
+	assert.Len(t, conditions, 0)
+}
+
+func createEnvironment() *Environment {
+
+	client, _ := test.NewFakeClient()
+	// disable the knative service api
+	fakeClient := client.(*test.FakeClient) //nolint
+	fakeClient.DisableAPIGroupDiscovery("serving.knative.dev/v1")
+
+	replicas := int32(3)
+	catalog, _ := camel.QuarkusCatalog()
+
+	environment := &Environment{
+		CamelCatalog: catalog,
+		Catalog:      NewCatalog(nil),
+		Client:       client,
+		Integration: &v1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "integration-name",
+			},
+			Spec: v1.IntegrationSpec{
+				Replicas: &replicas,
+				Traits:   v1.Traits{},
+			},
+			Status: v1.IntegrationStatus{
+				Phase: v1.IntegrationPhaseInitialization,
+			},
+		},
+		Platform: &v1.IntegrationPlatform{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "namespace",
+			},
+			Spec: v1.IntegrationPlatformSpec{
+				Cluster: v1.IntegrationPlatformClusterKubernetes,
+				Profile: v1.TraitProfileKubernetes,
+			},
+		},
+		Resources:             kubernetes.NewCollection(),
+		ApplicationProperties: make(map[string]string),
+	}
+	environment.Platform.ResyncStatusFullConfig()
+
+	return environment
+}
diff --git a/pkg/trait/cron_test.go b/pkg/trait/cron_test.go
index 9e30936d1..f1edb4a69 100644
--- a/pkg/trait/cron_test.go
+++ b/pkg/trait/cron_test.go
@@ -34,6 +34,7 @@ import (
 	"github.com/apache/camel-k/pkg/util"
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
 func TestCronFromURI(t *testing.T) {
@@ -225,11 +226,13 @@ func TestCronDeps(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "test",
@@ -295,11 +298,13 @@ func TestCronDepsFallback(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "test",
@@ -370,11 +375,13 @@ func TestCronWithActiveDeadline(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "test",
@@ -443,11 +450,13 @@ func TestCronWithBackoffLimit(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "test",
diff --git a/pkg/trait/deployment_test.go b/pkg/trait/deployment_test.go
index d0235e5af..eb1f06792 100644
--- a/pkg/trait/deployment_test.go
+++ b/pkg/trait/deployment_test.go
@@ -28,6 +28,7 @@ import (
 	"k8s.io/utils/pointer"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/util/test"
 )
@@ -265,10 +266,17 @@ func createNominalDeploymentTest() (*deploymentTrait, *Environment) {
 		},
 	})
 
+	// disable the knative service api
+	fakeClient := trait.Client.(*test.FakeClient) //nolint
+	fakeClient.DisableAPIGroupDiscovery("serving.knative.dev/v1")
+
 	replicas := int32(3)
+	catalog, _ := camel.QuarkusCatalog()
 
 	environment := &Environment{
-		Catalog: NewCatalog(nil),
+		CamelCatalog: catalog,
+		Catalog:      NewCatalog(nil),
+		Client:       trait.Client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: "integration-name",
diff --git a/pkg/trait/environment_test.go b/pkg/trait/environment_test.go
index 2072e8ede..d08b14041 100644
--- a/pkg/trait/environment_test.go
+++ b/pkg/trait/environment_test.go
@@ -31,6 +31,7 @@ import (
 	traitv1 "github.com/apache/camel-k/pkg/apis/camel/v1/trait"
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
 func TestDefaultEnvironment(t *testing.T) {
@@ -195,9 +196,11 @@ func NewEnvironmentTestCatalog() *Catalog {
 }
 
 func mockEnvironment(catalog *camel.RuntimeCatalog) Environment {
+	fakeClient, _ := test.NewFakeClient()
 	return Environment{
 		CamelCatalog: catalog,
 		Catalog:      NewCatalog(nil),
+		Client:       fakeClient,
 		Integration: &v1.Integration{
 			Status: v1.IntegrationStatus{
 				Phase: v1.IntegrationPhaseDeploying,
diff --git a/pkg/trait/istio_test.go b/pkg/trait/istio_test.go
index 75e537c9c..319a477c0 100644
--- a/pkg/trait/istio_test.go
+++ b/pkg/trait/istio_test.go
@@ -33,17 +33,19 @@ import (
 	traitv1 "github.com/apache/camel-k/pkg/apis/camel/v1/trait"
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
 func NewIstioTestEnv(t *testing.T, d *appsv1.Deployment, s *serving.Service, enabled bool) Environment {
 	t.Helper()
-
+	client, _ := test.NewFakeClient()
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
 	env := Environment{
 		Catalog:      NewEnvironmentTestCatalog(),
 		CamelCatalog: catalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			Status: v1.IntegrationStatus{
 				Phase: v1.IntegrationPhaseDeploying,
diff --git a/pkg/trait/knative_service_test.go b/pkg/trait/knative_service_test.go
index 1cab8fdff..193dd4615 100644
--- a/pkg/trait/knative_service_test.go
+++ b/pkg/trait/knative_service_test.go
@@ -46,6 +46,7 @@ func TestKnativeService(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	compressedRoute, err := gzip.CompressBase64([]byte(`from("undertow:test").log("hello")`))
@@ -54,6 +55,7 @@ func TestKnativeService(t *testing.T) {
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      KnativeServiceTestName,
@@ -189,11 +191,13 @@ func TestKnativeServiceWithCustomContainerName(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      KnativeServiceTestName,
@@ -266,11 +270,13 @@ func TestKnativeServiceWithResr(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      KnativeServiceTestName,
@@ -364,11 +370,13 @@ func createKnativeServiceTestEnvironment(t *testing.T, trait *traitv1.KnativeSer
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := &Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      KnativeServiceTestName,
diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go
index de4c6d42d..82930ebea 100644
--- a/pkg/trait/knative_test.go
+++ b/pkg/trait/knative_test.go
@@ -357,6 +357,7 @@ func TestKnativePlatformHttpDependencies(t *testing.T) {
 func NewFakeEnvironment(t *testing.T, source v1.SourceSpec) Environment {
 	t.Helper()
 
+	client, _ := NewFakeClient("ns")
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
@@ -365,6 +366,7 @@ func NewFakeEnvironment(t *testing.T, source v1.SourceSpec) Environment {
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "test",
diff --git a/pkg/trait/logging_test.go b/pkg/trait/logging_test.go
index e59563877..3a79e18ab 100644
--- a/pkg/trait/logging_test.go
+++ b/pkg/trait/logging_test.go
@@ -31,11 +31,13 @@ import (
 	traitv1 "github.com/apache/camel-k/pkg/apis/camel/v1/trait"
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
 func createLoggingTestEnv(t *testing.T, color bool, json bool, jsonPrettyPrint bool, logLevel string, logFormat string) *Environment {
 	t.Helper()
 
+	client, _ := test.NewFakeClient()
 	c, err := camel.DefaultCatalog()
 	if err != nil {
 		panic(err)
@@ -45,6 +47,7 @@ func createLoggingTestEnv(t *testing.T, color bool, json bool, jsonPrettyPrint b
 		Ctx:          context.TODO(),
 		CamelCatalog: c,
 		Catalog:      NewCatalog(nil),
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "test",
diff --git a/pkg/trait/route_test.go b/pkg/trait/route_test.go
index 9ddf24e5f..a83839e0b 100644
--- a/pkg/trait/route_test.go
+++ b/pkg/trait/route_test.go
@@ -141,6 +141,7 @@ func createTestRouteEnvironment(t *testing.T, name string) *Environment {
 	res := &Environment{
 		CamelCatalog: catalog,
 		Catalog:      NewCatalog(client),
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      name,
diff --git a/pkg/trait/service_test.go b/pkg/trait/service_test.go
index 081afec92..7b222f0cc 100644
--- a/pkg/trait/service_test.go
+++ b/pkg/trait/service_test.go
@@ -32,6 +32,7 @@ import (
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/gzip"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
 const (
@@ -43,6 +44,7 @@ func TestServiceWithDefaults(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	compressedRoute, err := gzip.CompressBase64([]byte(`from("netty-http:test").log("hello")`))
@@ -51,6 +53,7 @@ func TestServiceWithDefaults(t *testing.T) {
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      ServiceTestName,
@@ -136,6 +139,7 @@ func TestService(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	compressedRoute, err := gzip.CompressBase64([]byte(`from("netty-http:test").log("hello")`))
@@ -144,6 +148,7 @@ func TestService(t *testing.T) {
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      ServiceTestName,
@@ -237,11 +242,13 @@ func TestServiceWithCustomContainerName(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      ServiceTestName,
@@ -310,6 +317,7 @@ func TestServiceWithNodePort(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	compressedRoute, err := gzip.CompressBase64([]byte(`from("netty-http:test").log("hello")`))
@@ -318,6 +326,7 @@ func TestServiceWithNodePort(t *testing.T) {
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      ServiceTestName,
@@ -399,6 +408,7 @@ func TestServiceWithKnativeServiceEnabled(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	compressedRoute, err := gzip.CompressBase64([]byte(`from("netty-http:test").log("hello")`))
@@ -407,6 +417,7 @@ func TestServiceWithKnativeServiceEnabled(t *testing.T) {
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      ServiceTestName,
@@ -473,6 +484,7 @@ func TestServicesWithKnativeProfile(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	compressedRoute, err := gzip.CompressBase64([]byte(`from("netty-http:test").log("hello")`))
@@ -481,6 +493,7 @@ func TestServicesWithKnativeProfile(t *testing.T) {
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      ServiceTestName,
@@ -535,6 +548,7 @@ func TestServiceWithKnativeServiceDisabledInIntegrationPlatform(t *testing.T) {
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
+	client, _ := test.NewFakeClient()
 	traitCatalog := NewCatalog(nil)
 
 	compressedRoute, err := gzip.CompressBase64([]byte(`from("netty-http:test").log("hello")`))
@@ -543,6 +557,7 @@ func TestServiceWithKnativeServiceDisabledInIntegrationPlatform(t *testing.T) {
 	environment := Environment{
 		CamelCatalog: catalog,
 		Catalog:      traitCatalog,
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      ServiceTestName,
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index 78d4baac8..5a9350437 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -34,6 +34,7 @@ import (
 	traitv1 "github.com/apache/camel-k/pkg/apis/camel/v1/trait"
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
 const (
@@ -531,12 +532,14 @@ func processTestEnv(t *testing.T, env *Environment) *kubernetes.Collection {
 func createTestEnv(t *testing.T, cluster v1.IntegrationPlatformCluster, script string) *Environment {
 	t.Helper()
 
+	client, _ := test.NewFakeClient()
 	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 
 	res := &Environment{
 		CamelCatalog: catalog,
 		Catalog:      NewCatalog(nil),
+		Client:       client,
 		Integration: &v1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      TestDeploymentName,
diff --git a/pkg/util/test/client.go b/pkg/util/test/client.go
index e302fca9a..dc76713eb 100644
--- a/pkg/util/test/client.go
+++ b/pkg/util/test/client.go
@@ -28,6 +28,7 @@ import (
 	fakecamelclientset "github.com/apache/camel-k/pkg/client/camel/clientset/versioned/fake"
 	camelv1 "github.com/apache/camel-k/pkg/client/camel/clientset/versioned/typed/camel/v1"
 	camelv1alpha1 "github.com/apache/camel-k/pkg/client/camel/clientset/versioned/typed/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/util"
 	autoscalingv1 "k8s.io/api/autoscaling/v1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -121,8 +122,9 @@ func filterObjects(scheme *runtime.Scheme, input []runtime.Object, filter func(g
 type FakeClient struct {
 	controller.Client
 	kubernetes.Interface
-	camel  camel.Interface
-	scales *fakescale.FakeScaleClient
+	camel          camel.Interface
+	scales         *fakescale.FakeScaleClient
+	disabledGroups []string
 }
 
 func (c *FakeClient) CamelV1() camelv1.CamelV1Interface {
@@ -151,9 +153,14 @@ func (c *FakeClient) Patch(ctx context.Context, obj controller.Object, patch con
 	return c.Create(ctx, obj)
 }
 
+func (c *FakeClient) DisableAPIGroupDiscovery(group string) {
+	c.disabledGroups = append(c.disabledGroups, group)
+}
+
 func (c *FakeClient) Discovery() discovery.DiscoveryInterface {
 	return &FakeDiscovery{
 		DiscoveryInterface: c.Interface.Discovery(),
+		disabledGroups:     c.disabledGroups,
 	}
 }
 
@@ -169,6 +176,7 @@ func (c *FakeClient) ScalesClient() (scale.ScalesGetter, error) {
 
 type FakeDiscovery struct {
 	discovery.DiscoveryInterface
+	disabledGroups []string
 }
 
 func (f *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
@@ -178,5 +186,12 @@ func (f *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*me
 			Group: "image.openshift.io",
 		}, "")
 	}
+
+	// used in util/knative/enabled.go to verify if knative is installed
+	if groupVersion == "serving.knative.dev/v1" && !util.StringSliceExists(f.disabledGroups, groupVersion) {
+		return &metav1.APIResourceList{
+			GroupVersion: "serving.knative.dev/v1",
+		}, nil
+	}
 	return f.DiscoveryInterface.ServerResourcesForGroupVersion(groupVersion)
 }