Repository: apache/camel-k
Pull request #4354 (2023-05-09 17:36:55)

[Backport 1.10.x] fix(#4336): Restrict Knative binding provider execution
Backport of #4344

christophd: @squakez I have fixed the compile issues. mind triggering the workflow once more please

Patch:
diff --git a/pkg/cmd/operator/operator.go b/pkg/cmd/operator/operator.go
index ec0ee71ae..5018fb7ec 100644
--- a/pkg/cmd/operator/operator.go
+++ b/pkg/cmd/operator/operator.go
@@ -263,6 +263,7 @@ func findOrCreateIntegrationPlatform(ctx context.Context, c client.Client, opera
 		if defaultPlatform.Labels == nil {
 			defaultPlatform.Labels = make(map[string]string)
 		}
+		defaultPlatform.Labels["app"] = "camel-k"
 		defaultPlatform.Labels["camel.apache.org/platform.generated"] = "true"
 
 		if operatorID != "" {
diff --git a/pkg/trait/platform.go b/pkg/trait/platform.go
index 635bc1258..0c3823b6b 100644
--- a/pkg/trait/platform.go
+++ b/pkg/trait/platform.go
@@ -117,6 +117,7 @@ func (t *platformTrait) getOrCreatePlatform(e *Environment) (*v1.IntegrationPlat
 			if defaultPlatform.Labels == nil {
 				defaultPlatform.Labels = make(map[string]string)
 			}
+			defaultPlatform.Labels["app"] = "camel-k"
 			defaultPlatform.Labels["camel.apache.org/platform.generated"] = True
 			// Cascade the operator id in charge to reconcile the Integration
 			if v1.GetOperatorIDAnnotation(e.Integration) != "" {
diff --git a/pkg/util/bindings/knative_ref.go b/pkg/util/bindings/knative_ref.go
index a7ba2accd..f5758102e 100644
--- a/pkg/util/bindings/knative_ref.go
+++ b/pkg/util/bindings/knative_ref.go
@@ -26,6 +26,8 @@ import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/util/knative"
 	"github.com/apache/camel-k/pkg/util/uri"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 )
 
 // KnativeRefBindingProvider converts a reference to a Kubernetes object into a Camel URI.
@@ -42,6 +44,17 @@ func (k KnativeRefBindingProvider) Translate(ctx BindingContext, endpointCtx End
 		return nil, nil
 	}
 
+	if ok, err := isKnownKnativeResource(e.Ref); !ok {
+		// only operates on known Knative endpoint resources (e.g. channels, brokers)
+		return nil, err
+	}
+
+	if knativeInstalled, _ := knative.IsInstalled(ctx.Ctx, ctx.Client); !knativeInstalled {
+		// works only when Knative is installed
+		return nil, fmt.Errorf("integration referencing Knative endpoint '%s' that cannot run, "+
+			"because Knative is not installed on the cluster", e.Ref.Name)
+	}
+
 	serviceType, err := knative.GetServiceType(*e.Ref)
 	if err != nil {
 		return nil, err
@@ -93,6 +106,21 @@ func (k KnativeRefBindingProvider) Translate(ctx BindingContext, endpointCtx End
 	}, nil
 }
 
+func isKnownKnativeResource(ref *corev1.ObjectReference) (bool, error) {
+	gv, err := schema.ParseGroupVersion(ref.APIVersion)
+	if err != nil {
+		return false, err
+	}
+
+	for _, endpoint := range knative.KnownEndpointKinds {
+		if endpoint.Group == gv.Group && endpoint.Kind == ref.Kind {
+			return true, nil
+		}
+	}
+
+	return false, nil
+}
+
 func (k KnativeRefBindingProvider) Order() int {
 	// Executes as last, as it can be used as fallback for all unknown object references
 	return OrderLast


Test patch:
diff --git a/pkg/trait/platform_test.go b/pkg/trait/platform_test.go
index 02fe8e4f1..a1bb6b41c 100644
--- a/pkg/trait/platform_test.go
+++ b/pkg/trait/platform_test.go
@@ -109,7 +109,7 @@ func TestPlatformTraitCreatesDefaultPlatform(t *testing.T) {
 	assert.Equal(t, v1.IntegrationPhaseWaitingForPlatform, e.Integration.Status.Phase)
 	assert.Equal(t, 1, len(e.Resources.Items()))
 	defPlatform := v1.NewIntegrationPlatform("ns1", platform.DefaultPlatformName)
-	defPlatform.Labels = map[string]string{"camel.apache.org/platform.generated": True}
+	defPlatform.Labels = map[string]string{"app": "camel-k", "camel.apache.org/platform.generated": True}
 	assert.Contains(t, e.Resources.Items(), &defPlatform)
 }
 
diff --git a/pkg/util/bindings/knative_ref_test.go b/pkg/util/bindings/knative_ref_test.go
new file mode 100644
index 000000000..1933cf5f6
--- /dev/null
+++ b/pkg/util/bindings/knative_ref_test.go
@@ -0,0 +1,156 @@
+/*
+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 bindings
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	"github.com/apache/camel-k/pkg/util/test"
+	"github.com/stretchr/testify/assert"
+	v1 "k8s.io/api/core/v1"
+
+	camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+)
+
+func TestKnativeRefBinding(t *testing.T) {
+	testcases := []struct {
+		endpoint v1alpha1.Endpoint
+		uri      string
+	}{
+		{
+			endpoint: v1alpha1.Endpoint{
+				Ref: &v1.ObjectReference{
+					Kind:       "Broker",
+					Name:       "default",
+					APIVersion: "eventing.knative.dev/v1",
+				},
+			},
+			uri: "knative:event?apiVersion=eventing.knative.dev%2Fv1&kind=Broker&name=default",
+		},
+		{
+			endpoint: v1alpha1.Endpoint{
+				Ref: &v1.ObjectReference{
+					Kind:       "Channel",
+					Name:       "mychannel",
+					APIVersion: "messaging.knative.dev/v1",
+				},
+			},
+			uri: "knative:channel/mychannel?apiVersion=messaging.knative.dev%2Fv1&kind=Channel",
+		},
+		{
+			endpoint: v1alpha1.Endpoint{
+				Ref: &v1.ObjectReference{
+					Kind:       "Service",
+					Name:       "myservice",
+					APIVersion: "serving.knative.dev/v1",
+				},
+			},
+			uri: "knative:endpoint/myservice?apiVersion=serving.knative.dev%2Fv1&kind=Service",
+		},
+	}
+
+	for i, tc := range testcases {
+		t.Run(fmt.Sprintf("test-%d-%s", i, tc.endpoint.Ref.Kind), func(t *testing.T) {
+			ctx, cancel := context.WithCancel(context.Background())
+			defer cancel()
+
+			client, err := test.NewFakeClient()
+			assert.NoError(t, err)
+
+			bindingContext := BindingContext{
+				Ctx:       ctx,
+				Client:    client,
+				Namespace: "test",
+				Profile:   camelv1.TraitProfileKnative,
+			}
+
+			binding, err := KnativeRefBindingProvider{}.Translate(bindingContext, EndpointContext{
+				Type: v1alpha1.EndpointTypeSink,
+			}, tc.endpoint)
+			assert.NoError(t, err)
+			assert.NotNil(t, binding)
+			assert.Equal(t, tc.uri, binding.URI)
+			assert.Equal(t, camelv1.Traits{}, binding.Traits)
+		})
+	}
+}
+
+func TestUnsupportedKnativeResource(t *testing.T) {
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	client, err := test.NewFakeClient()
+	assert.NoError(t, err)
+
+	bindingContext := BindingContext{
+		Ctx:       ctx,
+		Client:    client,
+		Namespace: "test",
+		Profile:   camelv1.TraitProfileKnative,
+	}
+
+	endpoint := v1alpha1.Endpoint{
+		Ref: &v1.ObjectReference{
+			Kind:       "Unknown",
+			Name:       "default",
+			APIVersion: "eventing.knative.dev/v1",
+		},
+	}
+
+	binding, err := KnativeRefBindingProvider{}.Translate(bindingContext, EndpointContext{
+		Type: v1alpha1.EndpointTypeSink,
+	}, endpoint)
+	assert.NoError(t, err)
+	assert.Nil(t, binding)
+}
+
+func TestKnativeNotInstalled(t *testing.T) {
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	client, err := test.NewFakeClient()
+	assert.NoError(t, err)
+
+	// disable the knative service api
+	fakeClient := client.(*test.FakeClient) //nolint
+	fakeClient.DisableAPIGroupDiscovery("serving.knative.dev/v1")
+
+	bindingContext := BindingContext{
+		Ctx:       ctx,
+		Client:    client,
+		Namespace: "test",
+		Profile:   camelv1.TraitProfileKnative,
+	}
+
+	endpoint := v1alpha1.Endpoint{
+		Ref: &v1.ObjectReference{
+			Kind:       "Broker",
+			Name:       "default",
+			APIVersion: "eventing.knative.dev/v1",
+		},
+	}
+
+	binding, err := KnativeRefBindingProvider{}.Translate(bindingContext, EndpointContext{
+		Type: v1alpha1.EndpointTypeSink,
+	}, endpoint)
+	assert.Error(t, err, "integration referencing Knative endpoint 'default' that cannot run, because Knative is not installed on the cluster")
+	assert.Nil(t, binding)
+}