From ca5239b57c6e450b527204163d6cea33da297f3e Mon Sep 17 00:00:00 2001 From: Pasquale Congiusti Date: Fri, 14 Jun 2024 12:21:23 +0200 Subject: [PATCH] chore(ctrl): deprecate annotations trait The previous development was flawed, as it did not do the proper comparison with the configuration coming from annotation to the configuration coming from trait spec. This commit is supporting the comparison in a proper manner, however, we're deprecating the feature from other resources which are not Pipe as it does not make sense to support. Ref #5620 --- addons/addons_test.go | 36 ---- e2e/common/misc/pipe_with_image_test.go | 10 +- pkg/controller/integration/build_kit.go | 2 +- .../integration/integration_controller.go | 6 +- pkg/controller/integration/kits.go | 10 +- pkg/controller/integration/kits_test.go | 17 +- pkg/controller/integration/monitor.go | 24 +++ pkg/controller/integrationkit/monitor.go | 26 +++ pkg/controller/integrationplatform/monitor.go | 23 +++ .../kameletbinding_controller.go | 12 +- pkg/controller/kameletbinding/monitor.go | 2 +- pkg/controller/pipe/integration.go | 52 +----- pkg/controller/pipe/monitor.go | 2 +- pkg/controller/pipe/monitor_test.go | 48 +++++ pkg/controller/pipe/pipe_controller.go | 12 +- pkg/trait/quarkus.go | 6 - pkg/trait/quarkus_test.go | 19 -- pkg/trait/trait_configure.go | 6 + pkg/trait/util.go | 174 ++++++++++-------- pkg/trait/util_test.go | 67 +++---- pkg/util/digest/digest.go | 1 + 21 files changed, 295 insertions(+), 260 deletions(-) diff --git a/addons/addons_test.go b/addons/addons_test.go index 600b80c522..3adda05d21 100644 --- a/addons/addons_test.go +++ b/addons/addons_test.go @@ -20,8 +20,6 @@ package addons import ( "testing" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/apache/camel-k/v2/addons/master" "github.com/apache/camel-k/v2/addons/telemetry" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" @@ -68,37 +66,3 @@ func TestTraitConfiguration(t *testing.T) { require.True(t, ok) assert.True(t, *telemetry.Enabled) } - -func TestTraitConfigurationFromAnnotations(t *testing.T) { - env := trait.Environment{ - Integration: &v1.Integration{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - "trait.camel.apache.org/master.enabled": "true", - "trait.camel.apache.org/master.resource-name": "test-lock", - "trait.camel.apache.org/master.label-key": "test-label", - "trait.camel.apache.org/master.label-value": "test-value", - "trait.camel.apache.org/telemetry.enabled": "true", - }, - }, - Spec: v1.IntegrationSpec{ - Profile: v1.TraitProfileKubernetes, - }, - }, - } - c := trait.NewCatalog(nil) - require.NoError(t, c.Configure(&env)) - - require.NotNil(t, c.GetTrait("master")) - master, ok := c.GetTrait("master").(*master.TestMasterTrait) - require.True(t, ok) - assert.True(t, *master.Enabled) - assert.Equal(t, "test-lock", *master.ResourceName) - assert.Equal(t, "test-label", *master.LabelKey) - assert.Equal(t, "test-value", *master.LabelValue) - - require.NotNil(t, c.GetTrait("telemetry")) - telemetry, ok := c.GetTrait("telemetry").(*telemetry.TestTelemetryTrait) - require.True(t, ok) - assert.True(t, *telemetry.Enabled) -} diff --git a/e2e/common/misc/pipe_with_image_test.go b/e2e/common/misc/pipe_with_image_test.go index 0cb9b53b86..ff68b9b068 100644 --- a/e2e/common/misc/pipe_with_image_test.go +++ b/e2e/common/misc/pipe_with_image_test.go @@ -55,10 +55,9 @@ func TestPipeWithImage(t *testing.T) { g.Eventually(IntegrationGeneration(t, ctx, ns, bindingID)). Should(gstruct.PointTo(BeNumerically("==", 1))) - g.Eventually(Integration(t, ctx, ns, bindingID)).Should(WithTransform(Annotations, And( + g.Eventually(Integration(t, ctx, ns, bindingID)).Should(WithTransform(Annotations, HaveKeyWithValue("test", "1"), - HaveKeyWithValue("trait.camel.apache.org/container.image", expectedImage), - ))) + )) g.Eventually(IntegrationStatusImage(t, ctx, ns, bindingID)). Should(Equal(expectedImage)) g.Eventually(IntegrationPodPhase(t, ctx, ns, bindingID), TestTimeoutShort). @@ -73,10 +72,9 @@ func TestPipeWithImage(t *testing.T) { g.Expect(KamelBindWithID(t, ctx, operatorID, ns, "my-own-timer-source", "my-own-log-sink", "--annotation", "trait.camel.apache.org/container.image="+expectedImage, "--annotation", "trait.camel.apache.org/jvm.enabled=false", "--annotation", "trait.camel.apache.org/kamelets.enabled=false", "--annotation", "trait.camel.apache.org/dependencies.enabled=false", "--annotation", "test=2", "--name", bindingID).Execute()).To(Succeed()) g.Eventually(IntegrationGeneration(t, ctx, ns, bindingID)). Should(gstruct.PointTo(BeNumerically("==", 1))) - g.Eventually(Integration(t, ctx, ns, bindingID)).Should(WithTransform(Annotations, And( + g.Eventually(Integration(t, ctx, ns, bindingID)).Should(WithTransform(Annotations, HaveKeyWithValue("test", "2"), - HaveKeyWithValue("trait.camel.apache.org/container.image", expectedImage), - ))) + )) g.Eventually(IntegrationStatusImage(t, ctx, ns, bindingID)). Should(Equal(expectedImage)) g.Eventually(IntegrationPodPhase(t, ctx, ns, bindingID), TestTimeoutShort). diff --git a/pkg/controller/integration/build_kit.go b/pkg/controller/integration/build_kit.go index acbc01966a..af2d164ae2 100644 --- a/pkg/controller/integration/build_kit.go +++ b/pkg/controller/integration/build_kit.go @@ -99,7 +99,7 @@ kits: k := &existingKits[i] action.L.Debug("Comparing existing kit with environment", "env kit", kit.Name, "existing kit", k.Name) - match, err := kitMatches(&kit, k) + match, err := kitMatches(action.client, &kit, k) if err != nil { return nil, fmt.Errorf("error occurred matches integration kits with environment for integration %s/%s: %w", integration.Namespace, integration.Name, err) diff --git a/pkg/controller/integration/integration_controller.go b/pkg/controller/integration/integration_controller.go index 4fbdc39b4d..a3c895c2f9 100644 --- a/pkg/controller/integration/integration_controller.go +++ b/pkg/controller/integration/integration_controller.go @@ -85,7 +85,7 @@ func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler { ) } -func integrationUpdateFunc(old *v1.Integration, it *v1.Integration) bool { +func integrationUpdateFunc(c client.Client, old *v1.Integration, it *v1.Integration) bool { // Observe the time to first readiness metric previous := old.Status.GetCondition(v1.IntegrationConditionReady) next := it.Status.GetCondition(v1.IntegrationConditionReady) @@ -99,7 +99,7 @@ func integrationUpdateFunc(old *v1.Integration, it *v1.Integration) bool { updateIntegrationPhase(it.Name, string(it.Status.Phase)) // If traits have changed, the reconciliation loop must kick in as // traits may have impact - sameTraits, err := trait.IntegrationsHaveSameTraits(old, it) + sameTraits, err := trait.IntegrationsHaveSameTraits(c, old, it) if err != nil { Log.ForIntegration(it).Error( err, @@ -324,7 +324,7 @@ func add(ctx context.Context, mgr manager.Manager, c client.Client, r reconcile. return false } - return integrationUpdateFunc(old, it) + return integrationUpdateFunc(c, old, it) }, DeleteFunc: func(e event.DeleteEvent) bool { // Evaluates to false if the object has been confirmed deleted diff --git a/pkg/controller/integration/kits.go b/pkg/controller/integration/kits.go index 22adf0f039..9832139fa7 100644 --- a/pkg/controller/integration/kits.go +++ b/pkg/controller/integration/kits.go @@ -121,11 +121,11 @@ func integrationMatches(ctx context.Context, c client.Client, integration *v1.In return false, err } - itc, err := trait.NewSpecTraitsOptionsForIntegrationAndPlatform(integration, pl) + itc, err := trait.NewSpecTraitsOptionsForIntegrationAndPlatform(c, integration, pl) if err != nil { return false, err } - ikc, err := trait.NewSpecTraitsOptionsForIntegrationKit(kit) + ikc, err := trait.NewSpecTraitsOptionsForIntegrationKit(c, kit) if err != nil { return false, err } @@ -170,7 +170,7 @@ func statusMatches(integration *v1.Integration, kit *v1.IntegrationKit, ilog *lo } // kitMatches returns whether the kit matches with the existing target kit. -func kitMatches(kit *v1.IntegrationKit, target *v1.IntegrationKit) (bool, error) { +func kitMatches(c client.Client, kit *v1.IntegrationKit, target *v1.IntegrationKit) (bool, error) { version := kit.Status.Version if version == "" { // Defaults with the version that is going to be set during the kit initialization @@ -184,11 +184,11 @@ func kitMatches(kit *v1.IntegrationKit, target *v1.IntegrationKit) (bool, error) } // We cannot have yet the status set - c1, err := trait.NewSpecTraitsOptionsForIntegrationKit(kit) + c1, err := trait.NewSpecTraitsOptionsForIntegrationKit(c, kit) if err != nil { return false, err } - c2, err := trait.NewSpecTraitsOptionsForIntegrationKit(target) + c2, err := trait.NewSpecTraitsOptionsForIntegrationKit(c, target) if err != nil { return false, err } diff --git a/pkg/controller/integration/kits_test.go b/pkg/controller/integration/kits_test.go index 1f946aa350..9f10855263 100644 --- a/pkg/controller/integration/kits_test.go +++ b/pkg/controller/integration/kits_test.go @@ -25,6 +25,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/client" "github.com/apache/camel-k/v2/pkg/trait" "github.com/apache/camel-k/v2/pkg/util/test" @@ -279,7 +280,10 @@ func TestHasMatchingTraits_KitNoTraitShouldNotBePicked(t *testing.T) { }, } - ok, err := integrationAndKitHaveSameTraits(integration, kit) + c, err := test.NewFakeClient(integration, kit) + require.NoError(t, err) + + ok, err := integrationAndKitHaveSameTraits(c, integration, kit) require.NoError(t, err) assert.False(t, ok) } @@ -326,8 +330,9 @@ func TestHasMatchingTraits_KitSameTraitShouldBePicked(t *testing.T) { }, }, } - - ok, err := integrationAndKitHaveSameTraits(integration, kit) + c, err := test.NewFakeClient(integration, kit) + require.NoError(t, err) + ok, err := integrationAndKitHaveSameTraits(c, integration, kit) require.NoError(t, err) assert.True(t, ok) } @@ -429,12 +434,12 @@ func TestHasNotMatchingSources(t *testing.T) { assert.False(t, hsm2) } -func integrationAndKitHaveSameTraits(i1 *v1.Integration, i2 *v1.IntegrationKit) (bool, error) { - itOpts, err := trait.NewSpecTraitsOptionsForIntegration(i1) +func integrationAndKitHaveSameTraits(c client.Client, i1 *v1.Integration, i2 *v1.IntegrationKit) (bool, error) { + itOpts, err := trait.NewSpecTraitsOptionsForIntegration(c, i1) if err != nil { return false, err } - ikOpts, err := trait.NewSpecTraitsOptionsForIntegrationKit(i2) + ikOpts, err := trait.NewSpecTraitsOptionsForIntegrationKit(c, i2) if err != nil { return false, err } diff --git a/pkg/controller/integration/monitor.go b/pkg/controller/integration/monitor.go index 47bb9829a3..5d908cca6d 100644 --- a/pkg/controller/integration/monitor.go +++ b/pkg/controller/integration/monitor.go @@ -23,6 +23,7 @@ import ( "fmt" "reflect" "strconv" + "strings" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" @@ -131,9 +132,32 @@ func (action *monitorAction) Handle(ctx context.Context, integration *v1.Integra return integration, err } + action.checkTraitAnnotationsDeprecatedNotice(integration) + return action.monitorPods(ctx, environment, integration) } +// Deprecated: to be removed in future versions, when we won't support any longer trait annotations into Integrations. +func (action *monitorAction) checkTraitAnnotationsDeprecatedNotice(integration *v1.Integration) { + if integration.Annotations != nil { + for k := range integration.Annotations { + if strings.HasPrefix(k, v1.TraitAnnotationPrefix) { + integration.Status.SetCondition( + v1.IntegrationConditionType("AnnotationTraitsDeprecated"), + corev1.ConditionTrue, + "DeprecationNotice", + "Annotation traits configuration is deprecated and will be removed soon. Use .spec.traits configuration instead.", + ) + action.L.Infof( + "WARN: annotation traits configuration is deprecated and will be removed soon. Use .spec.traits configuration for %s integration instead.", + integration.Name, + ) + return + } + } + } +} + func (action *monitorAction) monitorPods(ctx context.Context, environment *trait.Environment, integration *v1.Integration) (*v1.Integration, error) { controller, err := action.newController(environment, integration) if err != nil { diff --git a/pkg/controller/integrationkit/monitor.go b/pkg/controller/integrationkit/monitor.go index 5b72ef1530..7a71d9a76c 100644 --- a/pkg/controller/integrationkit/monitor.go +++ b/pkg/controller/integrationkit/monitor.go @@ -19,9 +19,11 @@ package integrationkit import ( "context" + "strings" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" "github.com/apache/camel-k/v2/pkg/util/digest" + corev1 "k8s.io/api/core/v1" ) // NewMonitorAction creates a new monitoring handling action for the kit. @@ -64,5 +66,29 @@ func (action *monitorAction) Handle(ctx context.Context, kit *v1.IntegrationKit) return kit, nil } + action.checkTraitAnnotationsDeprecatedNotice(kit) + return nil, nil } + +// Deprecated: to be removed in future versions, when we won't support any longer trait annotations into IntegrationKits. +func (action *monitorAction) checkTraitAnnotationsDeprecatedNotice(integrationKit *v1.IntegrationKit) { + if integrationKit.Annotations != nil { + for k := range integrationKit.Annotations { + if strings.HasPrefix(k, v1.TraitAnnotationPrefix) { + integrationKit.Status.SetCondition( + v1.IntegrationKitConditionType("AnnotationTraitsDeprecated"), + corev1.ConditionTrue, + "DeprecationNotice", + "Annotation traits configuration is deprecated and will be removed soon. Use .spec.traits configuration instead.", + ) + + action.L.Infof( + "WARN: annotation traits configuration is deprecated and will be removed soon. Use .spec.traits configuration for %s integration kit instead.", + integrationKit.Name, + ) + return + } + } + } +} diff --git a/pkg/controller/integrationplatform/monitor.go b/pkg/controller/integrationplatform/monitor.go index 1156a80f96..3a3b3c25e6 100644 --- a/pkg/controller/integrationplatform/monitor.go +++ b/pkg/controller/integrationplatform/monitor.go @@ -20,6 +20,7 @@ package integrationplatform import ( "context" "fmt" + "strings" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" platformutil "github.com/apache/camel-k/v2/pkg/platform" @@ -124,6 +125,28 @@ func (action *monitorAction) Handle(ctx context.Context, platform *v1.Integratio } platform.Status.Phase = platformPhase + action.checkTraitAnnotationsDeprecatedNotice(platform) return platform, nil } + +// Deprecated: to be removed in future versions, when we won't support any longer trait annotations into IntegrationPlatforms. +func (action *monitorAction) checkTraitAnnotationsDeprecatedNotice(platform *v1.IntegrationPlatform) { + if platform.Annotations != nil { + for k := range platform.Annotations { + if strings.HasPrefix(k, v1.TraitAnnotationPrefix) { + platform.Status.SetCondition( + v1.IntegrationPlatformConditionType("AnnotationTraitsDeprecated"), + corev1.ConditionTrue, + "DeprecationNotice", + "Annotation traits configuration is deprecated and will be removed soon. Use .spec.traits configuration instead.", + ) + action.L.Infof( + "WARN: annotation traits configuration is deprecated and will be removed soon. Use .spec.traits configuration for %s platform instead.", + platform.Name, + ) + return + } + } + } +} diff --git a/pkg/controller/kameletbinding/kameletbinding_controller.go b/pkg/controller/kameletbinding/kameletbinding_controller.go index 2c6313a199..2da902b221 100644 --- a/pkg/controller/kameletbinding/kameletbinding_controller.go +++ b/pkg/controller/kameletbinding/kameletbinding_controller.go @@ -47,7 +47,7 @@ import ( // Add creates a new KameletBinding Controller and adds it to the Manager. The Manager will set fields on the Controller // and Start it when the Manager is Started. func Add(ctx context.Context, mgr manager.Manager, c client.Client) error { - return add(mgr, newReconciler(mgr, c)) + return add(mgr, newReconciler(mgr, c), c) } func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler { @@ -65,14 +65,14 @@ func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler { ) } -func add(mgr manager.Manager, r reconcile.Reconciler) error { - c, err := controller.New("kamelet-binding-controller", mgr, controller.Options{Reconciler: r}) +func add(mgr manager.Manager, r reconcile.Reconciler, c client.Client) error { + ctrl, err := controller.New("kamelet-binding-controller", mgr, controller.Options{Reconciler: r}) if err != nil { return err } // Watch for changes to primary resource KameletBinding - err = c.Watch(source.Kind(mgr.GetCache(), &v1alpha1.KameletBinding{}), + err = ctrl.Watch(source.Kind(mgr.GetCache(), &v1alpha1.KameletBinding{}), &handler.EnqueueRequestForObject{}, platform.FilteringFuncs{ UpdateFunc: func(e event.UpdateEvent) bool { @@ -87,7 +87,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { // If traits have changed, the reconciliation loop must kick in as // traits may have impact - sameTraits, err := trait.KameletBindingsHaveSameTraits(oldKameletBinding, newKameletBinding) + sameTraits, err := trait.KameletBindingsHaveSameTraits(c, oldKameletBinding, newKameletBinding) if err != nil { Log.ForKameletBinding(newKameletBinding).Error( err, @@ -114,7 +114,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { } // Watch Integration to propagate changes downstream - err = c.Watch(source.Kind(mgr.GetCache(), &v1.Integration{}), + err = ctrl.Watch(source.Kind(mgr.GetCache(), &v1.Integration{}), handler.EnqueueRequestForOwner( mgr.GetScheme(), mgr.GetRESTMapper(), diff --git a/pkg/controller/kameletbinding/monitor.go b/pkg/controller/kameletbinding/monitor.go index 2c6d409f89..8c0187fcf1 100644 --- a/pkg/controller/kameletbinding/monitor.go +++ b/pkg/controller/kameletbinding/monitor.go @@ -82,7 +82,7 @@ func (action *monitorAction) Handle(ctx context.Context, binding *v1alpha1.Kamel integrationProfileNamespaceChanged := v1.GetIntegrationProfileNamespaceAnnotation(binding) != "" && (v1.GetIntegrationProfileNamespaceAnnotation(binding) != v1.GetIntegrationProfileNamespaceAnnotation(&it)) - sameTraits, err := trait.IntegrationAndKameletBindingSameTraits(&it, binding) + sameTraits, err := trait.IntegrationAndKameletBindingSameTraits(action.client, &it, binding) if err != nil { return nil, err } diff --git a/pkg/controller/pipe/integration.go b/pkg/controller/pipe/integration.go index 8e8cc25c49..15f5a35562 100644 --- a/pkg/controller/pipe/integration.go +++ b/pkg/controller/pipe/integration.go @@ -22,7 +22,6 @@ import ( "encoding/json" "fmt" "sort" - "strings" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -88,10 +87,6 @@ func CreateIntegrationFor(ctx context.Context, c client.Client, binding *v1.Pipe it.Spec = *binding.Spec.Integration.DeepCopy() } - if &it.Spec != nil && traits != nil { - it.Spec = v1.IntegrationSpec{} - } - if traits != nil { it.Spec.Traits = *traits } @@ -226,52 +221,7 @@ func CreateIntegrationFor(ctx context.Context, c client.Client, binding *v1.Pipe // extractAndDeleteTraits will extract the annotation traits into v1.Traits struct, removing from the value from the input map. func extractAndDeleteTraits(c client.Client, annotations map[string]string) (*v1.Traits, error) { - // structure that will be marshalled into a v1.Traits as it was a kamel run command - catalog := trait.NewCatalog(c) - traitsPlainParams := []string{} - for k, v := range annotations { - if strings.HasPrefix(k, v1.TraitAnnotationPrefix) { - key := strings.ReplaceAll(k, v1.TraitAnnotationPrefix, "") - traitId := strings.Split(key, ".")[0] - if err := trait.ValidateTrait(catalog, traitId); err != nil { - return nil, err - } - traitArrayParams := extractAsArray(v) - for _, param := range traitArrayParams { - traitsPlainParams = append(traitsPlainParams, fmt.Sprintf("%s=%s", key, param)) - } - delete(annotations, k) - } - } - if len(traitsPlainParams) == 0 { - return nil, nil - } - var traits v1.Traits - if err := trait.ConfigureTraits(traitsPlainParams, &traits, catalog); err != nil { - return nil, err - } - - return &traits, nil -} - -// extractTraitValue can detect if the value is an array representation as ["prop1=1", "prop2=2"] and -// return an array with the values or with the single value passed as a parameter. -func extractAsArray(value string) []string { - if strings.HasPrefix(value, "[") && strings.HasSuffix(value, "]") { - arrayValue := []string{} - data := value[1 : len(value)-1] - vals := strings.Split(data, ",") - for _, v := range vals { - prop := strings.Trim(v, " ") - if strings.HasPrefix(prop, `"`) && strings.HasSuffix(prop, `"`) { - prop = prop[1 : len(prop)-1] - } - arrayValue = append(arrayValue, prop) - } - return arrayValue - } - - return []string{value} + return trait.ExtractAndMaybeDeleteTraits(c, annotations, true) } func configureBinding(integration *v1.Integration, bindings ...*bindings.Binding) error { diff --git a/pkg/controller/pipe/monitor.go b/pkg/controller/pipe/monitor.go index d8f2739aed..b7e109f3e9 100644 --- a/pkg/controller/pipe/monitor.go +++ b/pkg/controller/pipe/monitor.go @@ -72,7 +72,7 @@ func (action *monitorAction) Handle(ctx context.Context, pipe *v1.Pipe) (*v1.Pip integrationProfileNamespaceChanged := v1.GetIntegrationProfileNamespaceAnnotation(pipe) != "" && (v1.GetIntegrationProfileNamespaceAnnotation(pipe) != v1.GetIntegrationProfileNamespaceAnnotation(&it)) - sameTraits, err := trait.IntegrationAndPipeSameTraits(&it, pipe) + sameTraits, err := trait.IntegrationAndPipeSameTraits(action.client, &it, pipe) if err != nil { return nil, err } diff --git a/pkg/controller/pipe/monitor_test.go b/pkg/controller/pipe/monitor_test.go index a05f6e4dd5..a911599d69 100644 --- a/pkg/controller/pipe/monitor_test.go +++ b/pkg/controller/pipe/monitor_test.go @@ -312,8 +312,56 @@ func TestPipeIntegrationCreatingFromPipeCreatingPhase(t *testing.T) { // We calculate the integration the same way it does the operator // as we don't expect it to change in this test. it, err := CreateIntegrationFor(context.TODO(), c, pipe) + require.NoError(t, err) it.Status.Phase = v1.IntegrationPhaseBuildingKit + c, err = test.NewFakeClient(pipe, it) + require.NoError(t, err) + + a := NewMonitorAction() + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "monitor", a.Name()) + assert.True(t, a.CanHandle(pipe)) + handledPipe, err := a.Handle(context.TODO(), pipe) require.NoError(t, err) + assert.Equal(t, v1.PipePhaseCreating, handledPipe.Status.Phase) + assert.Equal(t, corev1.ConditionFalse, handledPipe.Status.GetCondition(v1.PipeConditionReady).Status) + assert.Equal(t, "Integration \"my-pipe\" is in \"Creating\" phase", handledPipe.Status.GetCondition(v1.PipeConditionReady).Message) +} + +func TestPipeIntegrationPipeTraitAnnotations(t *testing.T) { + pipe := &v1.Pipe{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.PipeKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-pipe", + Annotations: map[string]string{ + "trait.camel.apache.org/camel.runtime-version": "1.2.3", + }, + }, + Spec: v1.PipeSpec{ + Source: v1.Endpoint{ + URI: pointer.String("timer:tick"), + }, + Sink: v1.Endpoint{ + URI: pointer.String("log:info"), + }, + }, + Status: v1.PipeStatus{ + Phase: v1.PipePhaseCreating, + }, + } + + c, err := test.NewFakeClient(pipe) + require.NoError(t, err) + // We calculate the integration the same way it does the operator + // as we don't expect it to change in this test. + it, err := CreateIntegrationFor(context.TODO(), c, pipe) + require.NoError(t, err) + it.Status.Phase = v1.IntegrationPhaseBuildingKit c, err = test.NewFakeClient(pipe, it) require.NoError(t, err) diff --git a/pkg/controller/pipe/pipe_controller.go b/pkg/controller/pipe/pipe_controller.go index 3060cefc5b..9ad801b8da 100644 --- a/pkg/controller/pipe/pipe_controller.go +++ b/pkg/controller/pipe/pipe_controller.go @@ -47,7 +47,7 @@ import ( // Add creates a new Pipe Controller and adds it to the Manager. The Manager will set fields on the Controller // and Start it when the Manager is Started. func Add(ctx context.Context, mgr manager.Manager, c client.Client) error { - return add(mgr, newReconciler(mgr, c)) + return add(mgr, newReconciler(mgr, c), c) } func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler { @@ -65,14 +65,14 @@ func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler { ) } -func add(mgr manager.Manager, r reconcile.Reconciler) error { - c, err := controller.New("pipe-controller", mgr, controller.Options{Reconciler: r}) +func add(mgr manager.Manager, r reconcile.Reconciler, c client.Client) error { + ctrl, err := controller.New("pipe-controller", mgr, controller.Options{Reconciler: r}) if err != nil { return err } // Watch for changes to primary resource Pipe - err = c.Watch(source.Kind(mgr.GetCache(), &v1.Pipe{}), + err = ctrl.Watch(source.Kind(mgr.GetCache(), &v1.Pipe{}), &handler.EnqueueRequestForObject{}, platform.FilteringFuncs{ UpdateFunc: func(e event.UpdateEvent) bool { @@ -87,7 +87,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { // If traits have changed, the reconciliation loop must kick in as // traits may have impact - sameTraits, err := trait.PipesHaveSameTraits(oldPipe, newPipe) + sameTraits, err := trait.PipesHaveSameTraits(c, oldPipe, newPipe) if err != nil { Log.ForPipe(newPipe).Error( err, @@ -114,7 +114,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { } // Watch Integration to propagate changes downstream - err = c.Watch(source.Kind(mgr.GetCache(), &v1.Integration{}), + err = ctrl.Watch(source.Kind(mgr.GetCache(), &v1.Integration{}), handler.EnqueueRequestForOwner( mgr.GetScheme(), mgr.GetRESTMapper(), diff --git a/pkg/trait/quarkus.go b/pkg/trait/quarkus.go index 08dd0aafa7..8d47446141 100644 --- a/pkg/trait/quarkus.go +++ b/pkg/trait/quarkus.go @@ -20,7 +20,6 @@ package trait import ( "fmt" "sort" - "strings" "github.com/rs/xid" @@ -277,11 +276,6 @@ func (t *quarkusTrait) newIntegrationKit(e *Environment, packageType quarkusPack v1.SetAnnotation(&kit.ObjectMeta, v1.IntegrationProfileNamespaceAnnotation, e.Integration.Namespace) } } - for k, v := range integration.Annotations { - if strings.HasPrefix(k, v1.TraitAnnotationPrefix) { - v1.SetAnnotation(&kit.ObjectMeta, k, v) - } - } operatorID := defaults.OperatorID() if operatorID != "" { kit.SetOperatorID(operatorID) diff --git a/pkg/trait/quarkus_test.go b/pkg/trait/quarkus_test.go index 473da5c449..6c0ae919d5 100644 --- a/pkg/trait/quarkus_test.go +++ b/pkg/trait/quarkus_test.go @@ -70,25 +70,6 @@ func TestApplyQuarkusTraitDefaultKitLayout(t *testing.T) { assert.Equal(t, environment.IntegrationKits[0].Labels[v1.IntegrationKitLayoutLabel], v1.IntegrationKitLayoutFastJar) } -func TestApplyQuarkusTraitAnnotationKitConfiguration(t *testing.T) { - quarkusTrait, environment := createNominalQuarkusTest() - environment.Integration.Status.Phase = v1.IntegrationPhaseBuildingKit - - v1.SetAnnotation(&environment.Integration.ObjectMeta, v1.TraitAnnotationPrefix+"quarkus.foo", "camel-k") - - configured, condition, err := quarkusTrait.Configure(environment) - assert.True(t, configured) - require.NoError(t, err) - assert.Nil(t, condition) - - err = quarkusTrait.Apply(environment) - require.NoError(t, err) - assert.Len(t, environment.IntegrationKits, 1) - assert.Equal(t, v1.IntegrationKitLayoutFastJar, environment.IntegrationKits[0].Labels[v1.IntegrationKitLayoutLabel]) - assert.Equal(t, "camel-k", environment.IntegrationKits[0].Annotations[v1.TraitAnnotationPrefix+"quarkus.foo"]) - -} - func TestQuarkusTraitBuildModeOrder(t *testing.T) { quarkusTrait, environment := createNominalQuarkusTest() quarkusTrait.Modes = []traitv1.QuarkusMode{traitv1.NativeQuarkusMode, traitv1.JvmQuarkusMode} diff --git a/pkg/trait/trait_configure.go b/pkg/trait/trait_configure.go index 8efb45a1cb..b14d40b774 100644 --- a/pkg/trait/trait_configure.go +++ b/pkg/trait/trait_configure.go @@ -36,6 +36,7 @@ func (c *Catalog) Configure(env *Environment) error { if err := c.configureTraits(env.Platform.Status.Traits); err != nil { return err } + // Deprecated: to be removed in future version if err := c.configureTraitsFromAnnotations(env.Platform.Annotations); err != nil { return err } @@ -49,6 +50,7 @@ func (c *Catalog) Configure(env *Environment) error { if err := c.configureTraits(env.IntegrationKit.Spec.Traits); err != nil { return err } + // Deprecated: to be removed in future version if err := c.configureTraitsFromAnnotations(env.IntegrationKit.Annotations); err != nil { return err } @@ -57,6 +59,7 @@ func (c *Catalog) Configure(env *Environment) error { if err := c.configureTraits(env.Integration.Spec.Traits); err != nil { return err } + // Deprecated: to be removed in future version if err := c.configureTraitsFromAnnotations(env.Integration.Annotations); err != nil { return err } @@ -117,6 +120,7 @@ func decodeTrait(in map[string]interface{}, target Trait) error { return json.Unmarshal(data, target) } +// Deprecated: to be removed in future versions. func (c *Catalog) configureTraitsFromAnnotations(annotations map[string]string) error { options := make(map[string]map[string]interface{}, len(annotations)) for k, v := range annotations { @@ -154,6 +158,7 @@ func (c *Catalog) configureTraitsFromAnnotations(annotations map[string]string) return c.configureFromOptions(options) } +// Deprecated: to be removed in future versions. func (c *Catalog) configureFromOptions(traits map[string]map[string]interface{}) error { for id, config := range traits { t := c.GetTrait(id) @@ -167,6 +172,7 @@ func (c *Catalog) configureFromOptions(traits map[string]map[string]interface{}) return nil } +// Deprecated: to be removed in future versions. func configureTrait(id string, config map[string]interface{}, trait interface{}) error { md := mapstructure.Metadata{} diff --git a/pkg/trait/util.go b/pkg/trait/util.go index 374d90e48b..94581dad47 100644 --- a/pkg/trait/util.go +++ b/pkg/trait/util.go @@ -27,8 +27,6 @@ import ( "sort" "strings" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ctrl "sigs.k8s.io/controller-runtime/pkg/client" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" @@ -348,12 +346,12 @@ func Equals(i1 Options, i2 Options) bool { } // IntegrationsHaveSameTraits return if traits are the same. -func IntegrationsHaveSameTraits(i1 *v1.Integration, i2 *v1.Integration) (bool, error) { - c1, err := NewSpecTraitsOptionsForIntegration(i1) +func IntegrationsHaveSameTraits(c client.Client, i1 *v1.Integration, i2 *v1.Integration) (bool, error) { + c1, err := NewSpecTraitsOptionsForIntegration(c, i1) if err != nil { return false, err } - c2, err := NewSpecTraitsOptionsForIntegration(i2) + c2, err := NewSpecTraitsOptionsForIntegration(c, i2) if err != nil { return false, err } @@ -362,12 +360,12 @@ func IntegrationsHaveSameTraits(i1 *v1.Integration, i2 *v1.Integration) (bool, e } // PipesHaveSameTraits return if traits are the same. -func PipesHaveSameTraits(i1 *v1.Pipe, i2 *v1.Pipe) (bool, error) { - c1, err := NewTraitsOptionsForPipe(i1) +func PipesHaveSameTraits(c client.Client, i1 *v1.Pipe, i2 *v1.Pipe) (bool, error) { + c1, err := NewTraitsOptionsForPipe(c, i1) if err != nil { return false, err } - c2, err := NewTraitsOptionsForPipe(i2) + c2, err := NewTraitsOptionsForPipe(c, i2) if err != nil { return false, err } @@ -377,12 +375,12 @@ func PipesHaveSameTraits(i1 *v1.Pipe, i2 *v1.Pipe) (bool, error) { // KameletBindingsHaveSameTraits return if traits are the same. // Deprecated. -func KameletBindingsHaveSameTraits(i1 *v1alpha1.KameletBinding, i2 *v1alpha1.KameletBinding) (bool, error) { - c1, err := NewTraitsOptionsForKameletBinding(i1) +func KameletBindingsHaveSameTraits(c client.Client, i1 *v1alpha1.KameletBinding, i2 *v1alpha1.KameletBinding) (bool, error) { + c1, err := NewTraitsOptionsForKameletBinding(c, i1) if err != nil { return false, err } - c2, err := NewTraitsOptionsForKameletBinding(i2) + c2, err := NewTraitsOptionsForKameletBinding(c, i2) if err != nil { return false, err } @@ -393,16 +391,15 @@ func KameletBindingsHaveSameTraits(i1 *v1alpha1.KameletBinding, i2 *v1alpha1.Kam // IntegrationAndPipeSameTraits return if traits are the same. // The comparison is done for the subset of traits defines on the binding as during the trait processing, // some traits may be added to the Integration i.e. knative configuration in case of sink binding. -func IntegrationAndPipeSameTraits(i1 *v1.Integration, i2 *v1.Pipe) (bool, error) { - itOpts, err := NewSpecTraitsOptionsForIntegration(i1) +func IntegrationAndPipeSameTraits(c client.Client, i1 *v1.Integration, i2 *v1.Pipe) (bool, error) { + itOpts, err := NewSpecTraitsOptionsForIntegration(c, i1) if err != nil { return false, err } - klbOpts, err := NewTraitsOptionsForPipe(i2) + klbOpts, err := NewTraitsOptionsForPipe(c, i2) if err != nil { return false, err } - toCompare := make(Options) for k := range klbOpts { if v, ok := itOpts[k]; ok { @@ -417,12 +414,12 @@ func IntegrationAndPipeSameTraits(i1 *v1.Integration, i2 *v1.Pipe) (bool, error) // The comparison is done for the subset of traits defines on the binding as during the trait processing, // some traits may be added to the Integration i.e. knative configuration in case of sink binding. // Deprecated. -func IntegrationAndKameletBindingSameTraits(i1 *v1.Integration, i2 *v1alpha1.KameletBinding) (bool, error) { - itOpts, err := NewSpecTraitsOptionsForIntegration(i1) +func IntegrationAndKameletBindingSameTraits(c client.Client, i1 *v1.Integration, i2 *v1alpha1.KameletBinding) (bool, error) { + itOpts, err := NewSpecTraitsOptionsForIntegration(c, i1) if err != nil { return false, err } - klbOpts, err := NewTraitsOptionsForKameletBinding(i2) + klbOpts, err := NewTraitsOptionsForKameletBinding(c, i2) if err != nil { return false, err } @@ -437,8 +434,17 @@ func IntegrationAndKameletBindingSameTraits(i1 *v1.Integration, i2 *v1alpha1.Kam return Equals(klbOpts, toCompare), nil } -func newTraitsOptions(opts Options, objectMeta *metav1.ObjectMeta) (Options, error) { - m2, err := FromAnnotations(objectMeta) +// newTraitsOptions will merge the traits annotations with the traits spec using the same format. +func newTraitsOptions(c client.Client, opts Options, annotations map[string]string) (Options, error) { + annotationTraits, err := ExtractAndMaybeDeleteTraits(c, annotations, false) + if err != nil { + return nil, err + } + if annotationTraits == nil { + return opts, nil + } + + m2, err := ToTraitMap(*annotationTraits) if err != nil { return nil, err } @@ -450,7 +456,59 @@ func newTraitsOptions(opts Options, objectMeta *metav1.ObjectMeta) (Options, err return opts, nil } -func NewSpecTraitsOptionsForIntegrationAndPlatform(i *v1.Integration, pl *v1.IntegrationPlatform) (Options, error) { +// ExtractAndDeleteTraits will extract the annotation traits into v1.Traits struct, removing from the value from the input map. +func ExtractAndMaybeDeleteTraits(c client.Client, annotations map[string]string, del bool) (*v1.Traits, error) { + // structure that will be marshalled into a v1.Traits as it was a kamel run command + catalog := NewCatalog(c) + traitsPlainParams := []string{} + for k, v := range annotations { + if strings.HasPrefix(k, v1.TraitAnnotationPrefix) { + key := strings.ReplaceAll(k, v1.TraitAnnotationPrefix, "") + traitID := strings.Split(key, ".")[0] + if err := ValidateTrait(catalog, traitID); err != nil { + return nil, err + } + traitArrayParams := extractAsArray(v) + for _, param := range traitArrayParams { + traitsPlainParams = append(traitsPlainParams, fmt.Sprintf("%s=%s", key, param)) + } + if del { + delete(annotations, k) + } + } + } + if len(traitsPlainParams) == 0 { + return nil, nil + } + var traits v1.Traits + if err := ConfigureTraits(traitsPlainParams, &traits, catalog); err != nil { + return nil, err + } + + return &traits, nil +} + +// extractTraitValue can detect if the value is an array representation as ["prop1=1", "prop2=2"] and +// return an array with the values or with the single value passed as a parameter. +func extractAsArray(value string) []string { + if strings.HasPrefix(value, "[") && strings.HasSuffix(value, "]") { + arrayValue := []string{} + data := value[1 : len(value)-1] + vals := strings.Split(data, ",") + for _, v := range vals { + prop := strings.Trim(v, " ") + if strings.HasPrefix(prop, `"`) && strings.HasSuffix(prop, `"`) { + prop = prop[1 : len(prop)-1] + } + arrayValue = append(arrayValue, prop) + } + return arrayValue + } + + return []string{value} +} + +func NewSpecTraitsOptionsForIntegrationAndPlatform(c client.Client, i *v1.Integration, pl *v1.IntegrationPlatform) (Options, error) { var options Options var err error if pl != nil { @@ -471,32 +529,44 @@ func NewSpecTraitsOptionsForIntegrationAndPlatform(i *v1.Integration, pl *v1.Int options[k] = v } - return newTraitsOptions(options, &i.ObjectMeta) + // Deprecated: to remove when we remove support for traits annotations. + // IMPORTANT: when we remove this we'll need to remove the cli from the func, + // which will bring to more cascade removal. It had to be introduced to support the deprecated feature + // in a properly manner (ie, comparing the spec.traits with annotations in a proper way). + return newTraitsOptions(c, options, i.ObjectMeta.Annotations) } -func NewSpecTraitsOptionsForIntegration(i *v1.Integration) (Options, error) { +func NewSpecTraitsOptionsForIntegration(c client.Client, i *v1.Integration) (Options, error) { m1, err := ToTraitMap(i.Spec.Traits) if err != nil { return nil, err } - return newTraitsOptions(m1, &i.ObjectMeta) + // Deprecated: to remove when we remove support for traits annotations. + // IMPORTANT: when we remove this we'll need to remove the cli from the func, + // which will bring to more cascade removal. It had to be introduced to support the deprecated feature + // in a properly manner (ie, comparing the spec.traits with annotations in a proper way). + return newTraitsOptions(c, m1, i.ObjectMeta.Annotations) } -func newTraitsOptionsForIntegrationKit(i *v1.IntegrationKit, traits v1.IntegrationKitTraits) (Options, error) { +func newTraitsOptionsForIntegrationKit(c client.Client, i *v1.IntegrationKit, traits v1.IntegrationKitTraits) (Options, error) { m1, err := ToTraitMap(traits) if err != nil { return nil, err } - return newTraitsOptions(m1, &i.ObjectMeta) + // Deprecated: to remove when we remove support for traits annotations. + // IMPORTANT: when we remove this we'll need to remove the cli from the func, + // which will bring to more cascade removal. It had to be introduced to support the deprecated feature + // in a properly manner (ie, comparing the spec.traits with annotations in a proper way). + return newTraitsOptions(c, m1, i.ObjectMeta.Annotations) } -func NewSpecTraitsOptionsForIntegrationKit(i *v1.IntegrationKit) (Options, error) { - return newTraitsOptionsForIntegrationKit(i, i.Spec.Traits) +func NewSpecTraitsOptionsForIntegrationKit(c client.Client, i *v1.IntegrationKit) (Options, error) { + return newTraitsOptionsForIntegrationKit(c, i, i.Spec.Traits) } -func NewTraitsOptionsForPipe(pipe *v1.Pipe) (Options, error) { +func NewTraitsOptionsForPipe(c client.Client, pipe *v1.Pipe) (Options, error) { options := Options{} if pipe.Spec.Integration != nil { @@ -510,11 +580,11 @@ func NewTraitsOptionsForPipe(pipe *v1.Pipe) (Options, error) { } } - return newTraitsOptions(options, &pipe.ObjectMeta) + return newTraitsOptions(c, options, pipe.ObjectMeta.Annotations) } // Deprecated. -func NewTraitsOptionsForKameletBinding(kb *v1alpha1.KameletBinding) (Options, error) { +func NewTraitsOptionsForKameletBinding(c client.Client, kb *v1alpha1.KameletBinding) (Options, error) { options := Options{} if kb.Spec.Integration != nil { @@ -528,47 +598,7 @@ func NewTraitsOptionsForKameletBinding(kb *v1alpha1.KameletBinding) (Options, er } } - return newTraitsOptions(options, &kb.ObjectMeta) -} - -func FromAnnotations(meta *metav1.ObjectMeta) (Options, error) { - options := make(Options) - for k, v := range meta.Annotations { - if strings.HasPrefix(k, v1.TraitAnnotationPrefix) { - configKey := strings.TrimPrefix(k, v1.TraitAnnotationPrefix) - if strings.Contains(configKey, ".") { - parts := strings.SplitN(configKey, ".", 2) - id := parts[0] - prop := parts[1] - if _, ok := options[id]; !ok { - options[id] = make(map[string]interface{}) - } - options[id][prop] = stringOrSlice(v) - } else { - return options, fmt.Errorf("wrong format for trait annotation %q: missing trait ID", k) - } - } - } - - return options, nil -} - -// stringOrSlice returns either a string or a slice with trimmed values when the input is -// represented as an array style (ie, [a,b,c]). -func stringOrSlice(val string) interface{} { - if val == "[]" { - // empty array - return []string{} - } - if strings.HasPrefix(val, "[") && strings.HasSuffix(val, "]") { - slice := strings.Split(val[1:len(val)-1], ",") - for i := range slice { - slice[i] = strings.Trim(slice[i], " ") - } - return slice - } else { - return val - } + return newTraitsOptions(c, options, kb.ObjectMeta.Annotations) } // verify if the integration in the Environment contains an endpoint. diff --git a/pkg/trait/util_test.go b/pkg/trait/util_test.go index d89262bbf9..6e56b15a11 100644 --- a/pkg/trait/util_test.go +++ b/pkg/trait/util_test.go @@ -25,6 +25,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/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -174,6 +175,8 @@ func TestToTrait(t *testing.T) { } func TestSameTraits(t *testing.T) { + c, err := test.NewFakeClient() + require.NoError(t, err) t.Run("empty traits", func(t *testing.T) { oldKlb := &v1.Pipe{ Spec: v1.PipeSpec{ @@ -190,7 +193,7 @@ func TestSameTraits(t *testing.T) { }, } - ok, err := PipesHaveSameTraits(oldKlb, newKlb) + ok, err := PipesHaveSameTraits(c, oldKlb, newKlb) require.NoError(t, err) assert.True(t, ok) }) @@ -219,7 +222,7 @@ func TestSameTraits(t *testing.T) { }, } - ok, err := PipesHaveSameTraits(oldKlb, newKlb) + ok, err := PipesHaveSameTraits(c, oldKlb, newKlb) require.NoError(t, err) assert.True(t, ok) }) @@ -248,7 +251,7 @@ func TestSameTraits(t *testing.T) { }, } - ok, err := PipesHaveSameTraits(oldKlb, newKlb) + ok, err := PipesHaveSameTraits(c, oldKlb, newKlb) require.NoError(t, err) assert.False(t, ok) }) @@ -273,7 +276,7 @@ func TestSameTraits(t *testing.T) { }, } - ok, err := PipesHaveSameTraits(oldKlb, newKlb) + ok, err := PipesHaveSameTraits(c, oldKlb, newKlb) require.NoError(t, err) assert.True(t, ok) }) @@ -294,7 +297,7 @@ func TestSameTraits(t *testing.T) { }, } - ok, err := PipesHaveSameTraits(oldKlb, newKlb) + ok, err := PipesHaveSameTraits(c, oldKlb, newKlb) require.NoError(t, err) assert.True(t, ok) }) @@ -319,7 +322,7 @@ func TestSameTraits(t *testing.T) { }, } - ok, err := PipesHaveSameTraits(oldKlb, newKlb) + ok, err := PipesHaveSameTraits(c, oldKlb, newKlb) require.NoError(t, err) assert.False(t, ok) }) @@ -340,7 +343,7 @@ func TestSameTraits(t *testing.T) { }, } - ok, err := PipesHaveSameTraits(oldKlb, newKlb) + ok, err := PipesHaveSameTraits(c, oldKlb, newKlb) require.NoError(t, err) assert.False(t, ok) }) @@ -392,46 +395,28 @@ func TestHasMathchingTraitsMissing(t *testing.T) { assert.True(t, b1) } -func TestFromAnnotationsPlain(t *testing.T) { - meta := metav1.ObjectMeta{ - Annotations: map[string]string{ - "trait.camel.apache.org/trait.prop1": "hello1", - "trait.camel.apache.org/trait.prop2": "hello2", +func TestIntegrationAndPipeSameTraits(t *testing.T) { + pipe := &v1.Pipe{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "trait.camel.apache.org/camel.runtime-version": "1.2.3", + }, }, } - opt, err := FromAnnotations(&meta) - require.NoError(t, err) - tt, ok := opt.Get("trait") - assert.True(t, ok) - assert.Equal(t, "hello1", tt["prop1"]) - assert.Equal(t, "hello2", tt["prop2"]) -} -func TestFromAnnotationsArray(t *testing.T) { - meta := metav1.ObjectMeta{ - Annotations: map[string]string{ - "trait.camel.apache.org/trait.prop1": "[hello,world]", - // The func should trim empty spaces as well - "trait.camel.apache.org/trait.prop2": "[\"hello=1\", \"world=2\"]", + integration := &v1.Integration{ + Spec: v1.IntegrationSpec{ + Traits: v1.Traits{ + Camel: &traitv1.CamelTrait{ + RuntimeVersion: "1.2.3", + }, + }, }, } - opt, err := FromAnnotations(&meta) + c, err := test.NewFakeClient(pipe, integration) require.NoError(t, err) - tt, ok := opt.Get("trait") - assert.True(t, ok) - assert.Equal(t, []string{"hello", "world"}, tt["prop1"]) - assert.Equal(t, []string{"\"hello=1\"", "\"world=2\""}, tt["prop2"]) -} -func TestFromAnnotationsArrayEmpty(t *testing.T) { - meta := metav1.ObjectMeta{ - Annotations: map[string]string{ - "trait.camel.apache.org/trait.prop": "[]", - }, - } - opt, err := FromAnnotations(&meta) + result, err := IntegrationAndPipeSameTraits(c, integration, pipe) require.NoError(t, err) - tt, ok := opt.Get("trait") - assert.True(t, ok) - assert.Equal(t, []string{}, tt["prop"]) + assert.True(t, result) } diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go index 946b399f15..9630467db8 100644 --- a/pkg/util/digest/digest.go +++ b/pkg/util/digest/digest.go @@ -336,6 +336,7 @@ func sortedTraitsMapKeys(m map[string]map[string]interface{}) []string { return res } +// Deprecated: to be removed in future versions. func sortedTraitAnnotationsKeys(it *v1.Integration) []string { res := make([]string, 0, len(it.Annotations)) for k := range it.Annotations {