diff --git a/pkg/util/bindings/knative_ref.go b/pkg/util/bindings/knative_ref.go index 2859692a45..0bd0d7bcf8 100644 --- a/pkg/util/bindings/knative_ref.go +++ b/pkg/util/bindings/knative_ref.go @@ -25,6 +25,8 @@ import ( v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" knativeapis "github.com/apache/camel-k/v2/pkg/apis/camel/v1/knative" v1alpha1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1alpha1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "github.com/apache/camel-k/v2/pkg/util/knative" "github.com/apache/camel-k/v2/pkg/util/uri" @@ -46,6 +48,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.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 @@ -97,6 +110,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 +} + // Order --. func (k KnativeRefBindingProvider) Order() int { // Executes as last, as it can be used as fallback for all unknown object references diff --git a/pkg/util/bindings/knative_ref_test.go b/pkg/util/bindings/knative_ref_test.go new file mode 100644 index 0000000000..9d06ebed3e --- /dev/null +++ b/pkg/util/bindings/knative_ref_test.go @@ -0,0 +1,155 @@ +/* +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/v2/pkg/util/test" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + + camelv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" +) + +func TestKnativeRefBinding(t *testing.T) { + testcases := []struct { + endpoint camelv1.Endpoint + uri string + }{ + { + endpoint: camelv1.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: camelv1.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: camelv1.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: camelv1.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 := camelv1.Endpoint{ + Ref: &v1.ObjectReference{ + Kind: "Unknown", + Name: "default", + APIVersion: "eventing.knative.dev/v1", + }, + } + + binding, err := KnativeRefBindingProvider{}.Translate(bindingContext, EndpointContext{ + Type: camelv1.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 := camelv1.Endpoint{ + Ref: &v1.ObjectReference{ + Kind: "Broker", + Name: "default", + APIVersion: "eventing.knative.dev/v1", + }, + } + + binding, err := KnativeRefBindingProvider{}.Translate(bindingContext, EndpointContext{ + Type: camelv1.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) +}