From 93954e181c69d12530adb81df391a6947a7904b0 Mon Sep 17 00:00:00 2001 From: Pasquale Congiusti Date: Sat, 23 Nov 2024 09:12:31 +0100 Subject: [PATCH] fix(binding): don't panic if no binding available Closes #5953 --- .../kameletbinding/initialize_test.go | 96 +++++++++++++++++++ pkg/controller/kameletbinding/integration.go | 6 +- pkg/util/bindings/catalog.go | 19 +++- 3 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 pkg/controller/kameletbinding/initialize_test.go diff --git a/pkg/controller/kameletbinding/initialize_test.go b/pkg/controller/kameletbinding/initialize_test.go new file mode 100644 index 0000000000..d8b8a3935e --- /dev/null +++ b/pkg/controller/kameletbinding/initialize_test.go @@ -0,0 +1,96 @@ +/* +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 kameletbinding + +import ( + "context" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + "github.com/apache/camel-k/v2/pkg/apis/camel/v1alpha1" + + "github.com/apache/camel-k/v2/pkg/util/log" + "github.com/apache/camel-k/v2/pkg/util/test" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewKLBUnsupportedRef(t *testing.T) { + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-svc", + Namespace: "ns", + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{}, + Selector: map[string]string{ + v1.IntegrationLabel: "my-klb", + }, + }, + } + klb := &v1alpha1.KameletBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1alpha1.SchemeGroupVersion.String(), + Kind: v1alpha1.KameletBindingKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-klb", + }, + Spec: v1alpha1.KameletBindingSpec{ + Source: v1alpha1.Endpoint{ + URI: ptr.To("timer:tick"), + }, + Sink: v1alpha1.Endpoint{ + Ref: &corev1.ObjectReference{ + APIVersion: svc.APIVersion, + Kind: svc.Kind, + Namespace: svc.Namespace, + Name: svc.Name, + }, + }, + }, + } + c, err := test.NewFakeClient(klb) + require.NoError(t, err) + + a := NewInitializeAction() + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "initialize", a.Name()) + assert.True(t, a.CanHandle(klb)) + handledKlb, err := a.Handle(context.TODO(), klb) + require.Error(t, err) + assert.Equal(t, "could not find any suitable binding provider for v1/Service my-svc in namespace ns. "+ + "Bindings available: [\"kamelet\" \"knative-uri\" \"strimzi\" \"camel-uri\" \"knative-ref\"]", err.Error()) + assert.Equal(t, v1alpha1.KameletBindingPhaseError, handledKlb.Status.Phase) + cond := handledKlb.Status.GetCondition(v1alpha1.KameletBindingIntegrationConditionError) + assert.NotNil(t, cond) + assert.Equal(t, corev1.ConditionFalse, cond.Status) + assert.Equal(t, "could not find any suitable binding provider for v1/Service my-svc in namespace ns. "+ + "Bindings available: [\"kamelet\" \"knative-uri\" \"strimzi\" \"camel-uri\" \"knative-ref\"]", cond.Message) +} diff --git a/pkg/controller/kameletbinding/integration.go b/pkg/controller/kameletbinding/integration.go index b28780de88..f0ebc37ca0 100644 --- a/pkg/controller/kameletbinding/integration.go +++ b/pkg/controller/kameletbinding/integration.go @@ -105,16 +105,16 @@ func CreateIntegrationFor(ctx context.Context, c client.Client, binding *v1alpha from, err := bindings.TranslateV1alpha1(bindingContext, endpointTypeSourceContext, binding.Spec.Source) if err != nil { - return nil, fmt.Errorf("could not determine source URI: %w", err) + return nil, err } to, err := bindings.TranslateV1alpha1(bindingContext, endpointTypeSinkContext, binding.Spec.Sink) if err != nil { - return nil, fmt.Errorf("could not determine sink URI: %w", err) + return nil, err } // error handler is optional errorHandler, err := maybeErrorHandler(binding.Spec.ErrorHandler, bindingContext) if err != nil { - return nil, fmt.Errorf("could not determine error handler: %w", err) + return nil, err } steps := make([]*bindings.Binding, 0, len(binding.Spec.Steps)) diff --git a/pkg/util/bindings/catalog.go b/pkg/util/bindings/catalog.go index 18bcb24837..ff61e96002 100644 --- a/pkg/util/bindings/catalog.go +++ b/pkg/util/bindings/catalog.go @@ -57,6 +57,8 @@ func V1alpha1RegisterBindingProvider(bp V1alpha1BindingProvider) { } // Translate execute all chained binding providers, returning the first success or the first error. +// +//nolint:dupl func Translate(ctx BindingContext, endpointCtx EndpointContext, endpoint v1.Endpoint) (*Binding, error) { availableBindings := make([]string, len(bindingProviders)) if err := validateEndpoint(ctx, endpoint); err != nil { @@ -101,18 +103,31 @@ func validateEndpoint(ctx BindingContext, e v1.Endpoint) error { // TranslateV1alpha1 execute all chained binding providers, returning the first success or the first error. // Deprecated. +// +//nolint:dupl func TranslateV1alpha1(ctx V1alpha1BindingContext, endpointCtx V1alpha1EndpointContext, endpoint v1alpha1.Endpoint) (*Binding, error) { + availableBindings := make([]string, len(v1alpha1BindingProviders)) if err := validateEndpointV1alpha1(ctx, endpoint); err != nil { return nil, err } - for _, bp := range v1alpha1BindingProviders { + for i, bp := range v1alpha1BindingProviders { + availableBindings[i] = bp.ID() b, err := bp.Translate(ctx, endpointCtx, endpoint) if b != nil || err != nil { return b, err } } - return nil, nil + + // If no success we return an error with the actual list of available binding providers + var errorMessage string + if endpoint.Ref != nil { + errorMessage = fmt.Sprintf("could not find any suitable binding provider for %s/%s %s in namespace %s. Bindings available: %q", + endpoint.Ref.APIVersion, endpoint.Ref.Kind, endpoint.Ref.Name, endpoint.Ref.Namespace, availableBindings) + } else if ptr.Deref(endpoint.URI, "") != "" { + errorMessage = fmt.Sprintf("could not find any suitable binding provider for %s", *endpoint.URI) + } + return nil, fmt.Errorf(errorMessage) } // Deprecated.