From 4589b8b3fbd172dc845142e02472112406dbab46 Mon Sep 17 00:00:00 2001 From: Christoph Deppisch Date: Wed, 24 Apr 2024 19:34:11 +0200 Subject: [PATCH] fix(#5402): Evaluate Knative profile based on Serving/Eventing installed - Use Knative profile when Serving or Eventing is installed on cluster - Make sure to enable Knative trait when Serving or Eventing is installed - Enable knative-service trait only when Knative Serving is installed - Garbage collect Serving and Eventing resources in gc trait only when Serving/Eventing is installed on the cluster - Do not use Serving service in Knative trigger when not installed on cluster - Use arbitrary Service as a subscriber in Knative trigger when Serving is not available --- pkg/controller/integration/platform_setup.go | 2 +- pkg/controller/kameletbinding/integration.go | 2 +- pkg/controller/pipe/integration.go | 2 +- pkg/trait/gc.go | 45 +++++++++++--------- pkg/trait/knative.go | 33 ++++++++++++-- pkg/trait/knative_service.go | 6 +++ pkg/util/knative/enabled.go | 17 +++++++- pkg/util/knative/knative.go | 6 +-- 8 files changed, 82 insertions(+), 31 deletions(-) diff --git a/pkg/controller/integration/platform_setup.go b/pkg/controller/integration/platform_setup.go index 2c578276de..e1851f7696 100644 --- a/pkg/controller/integration/platform_setup.go +++ b/pkg/controller/integration/platform_setup.go @@ -97,7 +97,7 @@ func determineBestTraitProfile(c client.Client, integration *v1.Integration, p * // Use platform spec profile if set return p.Spec.Profile, nil } - if ok, err := knative.IsServingInstalled(c); err != nil { + if ok, err := knative.IsInstalled(c); err != nil { return "", err } else if ok { return v1.TraitProfileKnative, nil diff --git a/pkg/controller/kameletbinding/integration.go b/pkg/controller/kameletbinding/integration.go index 1f46302777..b28780de88 100644 --- a/pkg/controller/kameletbinding/integration.go +++ b/pkg/controller/kameletbinding/integration.go @@ -248,7 +248,7 @@ func determineTraitProfile(ctx context.Context, c client.Client, binding *v1alph return pl.Spec.Profile, nil } } - if ok, err := knative.IsServingInstalled(c); err != nil { + if ok, err := knative.IsInstalled(c); err != nil { return "", err } else if ok { return v1.TraitProfileKnative, nil diff --git a/pkg/controller/pipe/integration.go b/pkg/controller/pipe/integration.go index 80b06d662a..aa2a9c4be4 100644 --- a/pkg/controller/pipe/integration.go +++ b/pkg/controller/pipe/integration.go @@ -248,7 +248,7 @@ func determineTraitProfile(ctx context.Context, c client.Client, binding *v1.Pip return pl.Spec.Profile, nil } } - if ok, err := knative.IsServingInstalled(c); err != nil { + if ok, err := knative.IsInstalled(c); err != nil { return "", err } else if ok { return v1.TraitProfileKnative, nil diff --git a/pkg/trait/gc.go b/pkg/trait/gc.go index e3e5e79e85..2670de3fd6 100644 --- a/pkg/trait/gc.go +++ b/pkg/trait/gc.go @@ -47,6 +47,7 @@ import ( v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" "github.com/apache/camel-k/v2/pkg/util" + "github.com/apache/camel-k/v2/pkg/util/knative" "github.com/apache/camel-k/v2/pkg/util/log" ) @@ -86,20 +87,6 @@ var ( Version: batchv1.SchemeGroupVersion.Version, }: {}, } - deletableTypesByProfile = map[v1.TraitProfile]map[schema.GroupVersionKind]struct{}{ - v1.TraitProfileKnative: { - schema.GroupVersionKind{ - Kind: "Service", - Group: "serving.knative.dev", - Version: "v1", - }: {}, - schema.GroupVersionKind{ - Kind: "Trigger", - Group: "eventing.knative.dev", - Version: "v1", - }: {}, - }, - } ) type gcTrait struct { @@ -160,12 +147,30 @@ func (t *gcTrait) garbageCollectResources(e *Environment) error { } profile := e.DetermineProfile() - if profileDeletableTypes, ok := deletableTypesByProfile[profile]; ok { - // copy profile related deletable types if not already present - for key, value := range profileDeletableTypes { - if _, found := deletableGVKs[key]; !found { - deletableGVKs[key] = value - } + deletableTypesByProfile := map[schema.GroupVersionKind]struct{}{} + + if profile == v1.TraitProfileKnative { + if ok, _ := knative.IsServingInstalled(e.Client); ok { + deletableTypesByProfile[schema.GroupVersionKind{ + Kind: "Service", + Group: "serving.knative.dev", + Version: "v1", + }] = struct{}{} + } + + if ok, _ := knative.IsEventingInstalled(e.Client); ok { + deletableTypesByProfile[schema.GroupVersionKind{ + Kind: "Trigger", + Group: "eventing.knative.dev", + Version: "v1", + }] = struct{}{} + } + } + + // copy profile related deletable types if not already present + for key, value := range deletableTypesByProfile { + if _, found := deletableGVKs[key]; !found { + deletableGVKs[key] = value } } diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go index 3328ec6501..03261dc33d 100644 --- a/pkg/trait/knative.go +++ b/pkg/trait/knative.go @@ -331,7 +331,9 @@ func (t *knativeTrait) configureEvents(e *Environment, env *knativeapi.CamelEnvi serviceName = "default" } servicePath := fmt.Sprintf("/events/%s", eventType) - t.createTrigger(e, ref, eventType, servicePath) + if triggerErr := t.createTrigger(e, ref, eventType, servicePath); triggerErr != nil { + return triggerErr + } if !env.ContainsService(serviceName, knativeapi.CamelEndpointKindSource, knativeapi.CamelServiceTypeEvent, ref.APIVersion, ref.Kind) { svc := knativeapi.CamelServiceDefinition{ @@ -475,7 +477,7 @@ func (t *knativeTrait) configureSinkBinding(e *Environment, env *knativeapi.Came return err } -func (t *knativeTrait) createTrigger(e *Environment, ref *corev1.ObjectReference, eventType string, path string) { +func (t *knativeTrait) createTrigger(e *Environment, ref *corev1.ObjectReference, eventType string, path string) error { // TODO extend to additional filters too, to filter them at source and not at destination found := e.Resources.HasKnativeTrigger(func(trigger *eventing.Trigger) bool { return trigger.Spec.Broker == ref.Name && @@ -486,9 +488,18 @@ func (t *knativeTrait) createTrigger(e *Environment, ref *corev1.ObjectReference if ref.Namespace == "" { ref.Namespace = e.Integration.Namespace } - trigger := knativeutil.CreateTrigger(*ref, e.Integration.Name, eventType, path) + serviceAPIVersion, err := t.determineServiceType(e) + if err != nil { + return err + } + trigger, err := knativeutil.CreateTrigger(*ref, e.Integration.Name, serviceAPIVersion, eventType, path) + if err != nil { + return err + } e.Resources.Add(trigger) } + + return nil } func (t *knativeTrait) ifServiceMissingDo( @@ -560,3 +571,19 @@ func (t *knativeTrait) extractServices(names []string, serviceType knativeapi.Ca sort.Strings(answer) return answer } + +func (t *knativeTrait) determineServiceType(e *Environment) (string, error) { + controllerStrategy, err := e.DetermineControllerStrategy() + if err != nil { + return "", err + } + + switch controllerStrategy { + case ControllerStrategyKnativeService: + return serving.SchemeGroupVersion.String(), nil + case ControllerStrategyDeployment: + return "v1", nil + default: + return "", fmt.Errorf("failed to create Knative trigger: unsupported controller strategy %s", controllerStrategy) + } +} diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go index a8e53a779e..0fee5f9da7 100644 --- a/pkg/trait/knative_service.go +++ b/pkg/trait/knative_service.go @@ -30,6 +30,7 @@ import ( v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" "github.com/apache/camel-k/v2/pkg/metadata" + "github.com/apache/camel-k/v2/pkg/util/knative" "github.com/apache/camel-k/v2/pkg/util/kubernetes" ) @@ -149,6 +150,11 @@ func (t *knativeServiceTrait) SelectControllerStrategy(e *Environment) (*Control return nil, nil } + // Knative serving is required + if ok, _ := knative.IsServingInstalled(e.Client); !ok { + return nil, nil + } + var sources []v1.SourceSpec var err error if sources, err = kubernetes.ResolveIntegrationSources(e.Ctx, t.Client, e.Integration, e.Resources); err != nil { diff --git a/pkg/util/knative/enabled.go b/pkg/util/knative/enabled.go index 0a6b6ced2c..3cb503d5f0 100644 --- a/pkg/util/knative/enabled.go +++ b/pkg/util/knative/enabled.go @@ -28,7 +28,7 @@ import ( // IsRefKindInstalled returns true if the cluster has the referenced Kind installed. func IsRefKindInstalled(c kubernetes.Interface, ref corev1.ObjectReference) (bool, error) { - if installed, err := isInstalled(c, ref.GroupVersionKind().GroupVersion()); err != nil { + if installed, err := isServerResourceAvailable(c, ref.GroupVersionKind().GroupVersion()); err != nil { return false, err } else if installed { return true, nil @@ -36,6 +36,19 @@ func IsRefKindInstalled(c kubernetes.Interface, ref corev1.ObjectReference) (boo return false, nil } +// IsInstalled returns true if we are connected to a cluster with either Knative Serving or Eventing installed. +func IsInstalled(c kubernetes.Interface) (bool, error) { + if ok, err := IsServingInstalled(c); ok { + return ok, err + } else if ok, err = IsEventingInstalled(c); ok { + return ok, err + } else if err != nil { + return false, err + } + + return false, nil +} + // IsServingInstalled returns true if we are connected to a cluster with Knative Serving installed. func IsServingInstalled(c kubernetes.Interface) (bool, error) { return IsRefKindInstalled(c, corev1.ObjectReference{ @@ -52,7 +65,7 @@ func IsEventingInstalled(c kubernetes.Interface) (bool, error) { }) } -func isInstalled(c kubernetes.Interface, api schema.GroupVersion) (bool, error) { +func isServerResourceAvailable(c kubernetes.Interface, api schema.GroupVersion) (bool, error) { _, err := c.Discovery().ServerResourcesForGroupVersion(api.String()) if err != nil && (k8serrors.IsNotFound(err) || util.IsUnknownAPIError(err)) { return false, nil diff --git a/pkg/util/knative/knative.go b/pkg/util/knative/knative.go index 1d5b7462a9..5c0fb15712 100644 --- a/pkg/util/knative/knative.go +++ b/pkg/util/knative/knative.go @@ -75,7 +75,7 @@ func CreateSubscription(channelReference corev1.ObjectReference, serviceName str } } -func CreateTrigger(brokerReference corev1.ObjectReference, serviceName string, eventType string, path string) *eventing.Trigger { +func CreateTrigger(brokerReference corev1.ObjectReference, serviceName string, serviceAPIVersion string, eventType string, path string) (*eventing.Trigger, error) { nameSuffix := "" var attributes map[string]string if eventType != "" { @@ -100,7 +100,7 @@ func CreateTrigger(brokerReference corev1.ObjectReference, serviceName string, e Broker: brokerReference.Name, Subscriber: duckv1.Destination{ Ref: &duckv1.KReference{ - APIVersion: serving.SchemeGroupVersion.String(), + APIVersion: serviceAPIVersion, Kind: "Service", Name: serviceName, }, @@ -109,7 +109,7 @@ func CreateTrigger(brokerReference corev1.ObjectReference, serviceName string, e }, }, }, - } + }, nil } func CreateSinkBinding(source corev1.ObjectReference, target corev1.ObjectReference) *sources.SinkBinding {