This is an automated email from the ASF dual-hosted git repository.

tsato pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 2dbdaf4a1fe9890e9eac6fd504e629db86208d30
Author: Tadayoshi Sato <[email protected]>
AuthorDate: Tue Jun 28 13:01:25 2022 +0900

    fix(api): restore TraitSpec/Configuration for backward compatibility
---
 e2e/global/common/secondary_platform_test.go    |  14 +-
 e2e/support/test_support.go                     |   6 +-
 go.mod                                          |   2 +-
 go.sum                                          |   3 +-
 pkg/apis/camel/v1/common_types.go               |  25 ++++
 pkg/apis/camel/v1/trait/base.go                 |  12 ++
 pkg/apis/camel/v1/trait/{base.go => support.go} |  27 +++-
 pkg/cmd/promote.go                              | 176 +++++++++---------------
 pkg/cmd/promote_test.go                         |  60 --------
 pkg/trait/test_support.go                       |  32 +++++
 pkg/trait/trait_configure.go                    |  16 ++-
 pkg/trait/trait_configure_test.go               |  26 ++++
 pkg/trait/trait_test.go                         |  18 ---
 script/Makefile                                 |   3 +
 14 files changed, 216 insertions(+), 204 deletions(-)

diff --git a/e2e/global/common/secondary_platform_test.go 
b/e2e/global/common/secondary_platform_test.go
index ad2cd1c9b..665262737 100644
--- a/e2e/global/common/secondary_platform_test.go
+++ b/e2e/global/common/secondary_platform_test.go
@@ -31,6 +31,7 @@ import (
 
        . "github.com/apache/camel-k/e2e/support"
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       traitv1 "github.com/apache/camel-k/pkg/apis/camel/v1/trait"
 )
 
 func TestSecondaryPlatform(t *testing.T) {
@@ -38,13 +39,12 @@ func TestSecondaryPlatform(t *testing.T) {
                Expect(KamelInstall(ns).Execute()).To(Succeed())
                Expect(ConfigureSecondayPlatfromWith(ns, func(p 
*v1.IntegrationPlatform) {
                        p.Name = "secondary"
-                       if p.Spec.Traits == nil {
-                               p.Spec.Traits = make(map[string]v1.TraitSpec)
-                       }
-                       p.Spec.Traits["container"] = v1.TraitSpec{
-                               Configuration: 
AsTraitConfiguration(map[string]string{
-                                       "limitCPU": "0.1",
-                               }),
+                       p.Spec.Traits.Container = &traitv1.ContainerTrait{
+                               Trait: traitv1.Trait{
+                                       Configuration: 
AsTraitConfiguration(map[string]string{
+                                               "limitCPU": "0.1",
+                                       }),
+                               },
                        }
                })).To(Succeed())
 
diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go
index cec5e580b..08f9d7455 100644
--- a/e2e/support/test_support.go
+++ b/e2e/support/test_support.go
@@ -71,6 +71,7 @@ import (
 
        "github.com/apache/camel-k/e2e/support/util"
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       traitv1 "github.com/apache/camel-k/pkg/apis/camel/v1/trait"
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
        "github.com/apache/camel-k/pkg/client"
        "github.com/apache/camel-k/pkg/cmd"
@@ -1764,12 +1765,13 @@ func asEndpointProperties(props map[string]string) 
*v1alpha1.EndpointProperties
        }
 }
 
-func AsTraitConfiguration(props map[string]string) v1.TraitConfiguration {
+// nolint: staticcheck
+func AsTraitConfiguration(props map[string]string) *traitv1.Configuration {
        bytes, err := json.Marshal(props)
        if err != nil {
                failTest(err)
        }
-       return v1.TraitConfiguration{
+       return &traitv1.Configuration{
                RawMessage: bytes,
        }
 }
diff --git a/go.mod b/go.mod
index 4ee008a89..c28f89e8b 100644
--- a/go.mod
+++ b/go.mod
@@ -57,7 +57,7 @@ require (
        k8s.io/gengo v0.0.0-20211129171323-c02415ce4185
        k8s.io/klog/v2 v2.40.1
        k8s.io/kubectl v0.22.5
-       k8s.io/utils v0.0.0-20211208161948-7d6a63dca704
+       k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
        knative.dev/eventing v0.29.0
        knative.dev/pkg v0.0.0-20220118160532-77555ea48cd4
        knative.dev/serving v0.29.0
diff --git a/go.sum b/go.sum
index 5bc79daf9..9783c2877 100644
--- a/go.sum
+++ b/go.sum
@@ -2134,8 +2134,9 @@ k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod 
h1:jPW/WVKK9YHAvNhRxK0md/
 k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod 
h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod 
h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod 
h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20211208161948-7d6a63dca704 
h1:ZKMMxTvduyf5WUtREOqg5LiXaN1KO/+0oOQPRFrClpo=
 k8s.io/utils v0.0.0-20211208161948-7d6a63dca704/go.mod 
h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 
h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
+k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod 
h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 knative.dev/caching v0.0.0-20220118175933-0c1cc094a7f4/go.mod 
h1:OHsK3XaWmdi2/mPaNBiX8LbefbtM6TptnT90eVbYGSA=
 knative.dev/eventing v0.29.0 h1:+AhcYEuuz37x1op71bknZ/lFgsvuZJl0AKFOOFiH03s=
 knative.dev/eventing v0.29.0/go.mod 
h1:u5T5NZTDUsLR7yJwp5MDnBnDX5MhywD3yK3Rq+7gTtI=
diff --git a/pkg/apis/camel/v1/common_types.go 
b/pkg/apis/camel/v1/common_types.go
index 83d95c93e..12a4122d7 100644
--- a/pkg/apis/camel/v1/common_types.go
+++ b/pkg/apis/camel/v1/common_types.go
@@ -173,6 +173,31 @@ type Traits struct {
        ServiceBinding *trait.ServiceBindingTrait `property:"service-binding" 
json:"service-binding,omitempty"`
        // The configuration of Toleration trait
        Toleration *trait.TolerationTrait `property:"toleration" 
json:"toleration,omitempty"`
+
+       // Deprecated: for backward compatibility.
+       Keda *TraitSpec `property:"keda" json:"keda,omitempty"`
+       // Deprecated: for backward compatibility.
+       Master *TraitSpec `property:"master" json:"master,omitempty"`
+       // Deprecated: for backward compatibility.
+       Strimzi *TraitSpec `property:"strimzi" json:"strimzi,omitempty"`
+       // Deprecated: for backward compatibility.
+       ThreeScale *TraitSpec `property:"3scale" json:"3scale,omitempty"`
+       // Deprecated: for backward compatibility.
+       Tracing *TraitSpec `property:"tracing" json:"tracing,omitempty"`
+}
+
+// A TraitSpec contains the configuration of a trait
+// Deprecated: superceded by each Trait type, left for backward compatibility.
+type TraitSpec struct {
+       // TraitConfiguration parameters configuration
+       Configuration TraitConfiguration `json:"configuration"`
+}
+
+// TraitConfiguration represents the expected configuration for a given trait 
parameter
+// Deprecated: superceded by each Trait type, left for backward compatibility.
+type TraitConfiguration struct {
+       // generic raw message, typically a map containing the keys (trait 
parameters) and the values (either single text or array)
+       RawMessage `json:",inline"`
 }
 
 // +kubebuilder:validation:Type=object
diff --git a/pkg/apis/camel/v1/trait/base.go b/pkg/apis/camel/v1/trait/base.go
index 863d17a2f..5117e1ac9 100644
--- a/pkg/apis/camel/v1/trait/base.go
+++ b/pkg/apis/camel/v1/trait/base.go
@@ -21,4 +21,16 @@ package trait
 type Trait struct {
        // Can be used to enable or disable a trait. All traits share this 
common property.
        Enabled *bool `property:"enabled" json:"enabled,omitempty"`
+
+       // Legacy trait configuration parameters.
+       // Deprecated: for backward compatibility.
+       Configuration *Configuration `json:"configuration,omitempty"`
+}
+
+// Deprecated: for backward compatibility.
+type Configuration struct {
+       RawMessage `json:",inline"`
 }
+
+// Deprecated: for backward compatibility.
+type RawMessage []byte
diff --git a/pkg/apis/camel/v1/trait/base.go 
b/pkg/apis/camel/v1/trait/support.go
similarity index 59%
copy from pkg/apis/camel/v1/trait/base.go
copy to pkg/apis/camel/v1/trait/support.go
index 863d17a2f..027fdc700 100644
--- a/pkg/apis/camel/v1/trait/base.go
+++ b/pkg/apis/camel/v1/trait/support.go
@@ -17,8 +17,27 @@ limitations under the License.
 
 package trait
 
-// Base type for all traits.
-type Trait struct {
-       // Can be used to enable or disable a trait. All traits share this 
common property.
-       Enabled *bool `property:"enabled" json:"enabled,omitempty"`
+import (
+       "encoding/json"
+       "errors"
+)
+
+// MarshalJSON returns m as the JSON encoding of m.
+func (m RawMessage) MarshalJSON() ([]byte, error) {
+       if m == nil {
+               return []byte("null"), nil
+       }
+       return m, nil
+}
+
+// UnmarshalJSON sets *m to a copy of data.
+func (m *RawMessage) UnmarshalJSON(data []byte) error {
+       if m == nil {
+               return errors.New("json.RawMessage: UnmarshalJSON on nil 
pointer")
+       }
+       *m = append((*m)[0:0], data...)
+       return nil
 }
+
+var _ json.Marshaler = (*RawMessage)(nil)
+var _ json.Unmarshaler = (*RawMessage)(nil)
diff --git a/pkg/cmd/promote.go b/pkg/cmd/promote.go
index c1fb1f3e4..db9159475 100644
--- a/pkg/cmd/promote.go
+++ b/pkg/cmd/promote.go
@@ -27,6 +27,7 @@ import (
        "github.com/pkg/errors"
 
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       traitv1 "github.com/apache/camel-k/pkg/apis/camel/v1/trait"
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
        "github.com/apache/camel-k/pkg/client"
        "github.com/apache/camel-k/pkg/util/camel"
@@ -123,20 +124,12 @@ func (o *promoteCmdOptions) run(cmd *cobra.Command, args 
[]string) error {
        }
        if promoteKameletBinding {
                // KameletBinding promotion
-               destKameletBinding, err := 
o.editKameletBinding(sourceKameletBinding, sourceIntegration)
-               if err != nil {
-                       return errors.Wrap(err, "could not edit KameletBinding 
"+name)
-               }
+               destKameletBinding := 
o.editKameletBinding(sourceKameletBinding, sourceIntegration)
 
                return c.Create(o.Context, destKameletBinding)
        }
        // Plain Integration promotion
-       destIntegration, err := o.editIntegration(sourceIntegration)
-       if err != nil {
-               if err != nil {
-                       return errors.Wrap(err, "could not edit Integration 
"+name)
-               }
-       }
+       destIntegration := o.editIntegration(sourceIntegration)
 
        // Ensure the destination namespace has access to the source namespace 
images
        err = addSystemPullerRoleBinding(o.Context, c, 
sourceIntegration.Namespace, destIntegration.Namespace)
@@ -194,82 +187,76 @@ func (o *promoteCmdOptions) validateDestResources(c 
client.Client, it *v1.Integr
        var pvcs []string
        var kamelets []string
 
-       if it.Spec.Traits != nil {
-               // Mount trait
-               mounts := it.Spec.Traits["mount"]
-               if err := json.Unmarshal(mounts.Configuration.RawMessage, 
&traits); err != nil {
-                       return err
-               }
-               for t, v := range traits {
-                       switch t {
-                       case "configs":
-                               for _, cn := range v {
-                                       if conf, parseErr := 
resource.ParseConfig(cn); parseErr == nil {
-                                               if conf.StorageType() == 
resource.StorageTypeConfigmap {
-                                                       configmaps = 
append(configmaps, conf.Name())
-                                               } else if conf.StorageType() == 
resource.StorageTypeSecret {
-                                                       secrets = 
append(secrets, conf.Name())
-                                               }
-                                       } else {
-                                               return parseErr
+       // Mount trait
+       mounts := it.Spec.Traits.Mount
+       if err := json.Unmarshal(mounts.Configuration.RawMessage, &traits); err 
!= nil {
+               return err
+       }
+       for t, v := range traits {
+               switch t {
+               case "configs":
+                       for _, cn := range v {
+                               if conf, parseErr := resource.ParseConfig(cn); 
parseErr == nil {
+                                       if conf.StorageType() == 
resource.StorageTypeConfigmap {
+                                               configmaps = append(configmaps, 
conf.Name())
+                                       } else if conf.StorageType() == 
resource.StorageTypeSecret {
+                                               secrets = append(secrets, 
conf.Name())
                                        }
+                               } else {
+                                       return parseErr
                                }
-                       case "resources":
-                               for _, cn := range v {
-                                       if conf, parseErr := 
resource.ParseResource(cn); parseErr == nil {
-                                               if conf.StorageType() == 
resource.StorageTypeConfigmap {
-                                                       configmaps = 
append(configmaps, conf.Name())
-                                               } else if conf.StorageType() == 
resource.StorageTypeSecret {
-                                                       secrets = 
append(secrets, conf.Name())
-                                               }
-                                       } else {
-                                               return parseErr
+                       }
+               case "resources":
+                       for _, cn := range v {
+                               if conf, parseErr := 
resource.ParseResource(cn); parseErr == nil {
+                                       if conf.StorageType() == 
resource.StorageTypeConfigmap {
+                                               configmaps = append(configmaps, 
conf.Name())
+                                       } else if conf.StorageType() == 
resource.StorageTypeSecret {
+                                               secrets = append(secrets, 
conf.Name())
                                        }
+                               } else {
+                                       return parseErr
                                }
-                       case "volumes":
-                               for _, cn := range v {
-                                       if conf, parseErr := 
resource.ParseVolume(cn); parseErr == nil {
-                                               if conf.StorageType() == 
resource.StorageTypePVC {
-                                                       pvcs = append(pvcs, 
conf.Name())
-                                               }
-                                       } else {
-                                               return parseErr
+                       }
+               case "volumes":
+                       for _, cn := range v {
+                               if conf, parseErr := resource.ParseVolume(cn); 
parseErr == nil {
+                                       if conf.StorageType() == 
resource.StorageTypePVC {
+                                               pvcs = append(pvcs, conf.Name())
                                        }
+                               } else {
+                                       return parseErr
                                }
                        }
                }
+       }
 
-               // Openapi trait
-               openapis := it.Spec.Traits["openapi"]
-
-               traits = map[string][]string{}
-               if len(openapis.Configuration.RawMessage) > 0 {
-                       if err := 
json.Unmarshal(openapis.Configuration.RawMessage, &traits); err != nil {
-                               return err
-                       }
+       // Openapi trait
+       openapis := it.Spec.Traits.OpenAPI
+       traits = map[string][]string{}
+       if len(openapis.Configuration.RawMessage) > 0 {
+               if err := json.Unmarshal(openapis.Configuration.RawMessage, 
&traits); err != nil {
+                       return err
                }
-
-               for k, v := range traits {
-                       for _, cn := range v {
-                               if k == "configmaps" {
-                                       configmaps = append(configmaps, cn)
-                               }
+       }
+       for k, v := range traits {
+               for _, cn := range v {
+                       if k == "configmaps" {
+                               configmaps = append(configmaps, cn)
                        }
                }
+       }
 
-               // Kamelet trait
-               kameletTrait := it.Spec.Traits["kamelets"]
-               var kameletListTrait map[string]string
-
-               if len(kameletTrait.Configuration.RawMessage) > 0 {
-                       if err := 
json.Unmarshal(kameletTrait.Configuration.RawMessage, &kameletListTrait); err 
!= nil {
-                               return err
-                       }
-
-                       kamelets = strings.Split(kameletListTrait["list"], ",")
+       // Kamelet trait
+       kameletTrait := it.Spec.Traits.Kamelets
+       var kameletListTrait map[string]string
+       if len(kameletTrait.Configuration.RawMessage) > 0 {
+               if err := json.Unmarshal(kameletTrait.Configuration.RawMessage, 
&kameletListTrait); err != nil {
+                       return err
                }
-       } // end of it.Spec.Traits != nil
 
+               kamelets = strings.Split(kameletListTrait["list"], ",")
+       }
        sourceKamelets, err := o.listKamelets(c, it)
        if err != nil {
                return err
@@ -391,30 +378,28 @@ func existsKamelet(ctx context.Context, c client.Client, 
name string, namespace
        return true
 }
 
-func (o *promoteCmdOptions) editIntegration(it *v1.Integration) 
(*v1.Integration, error) {
+func (o *promoteCmdOptions) editIntegration(it *v1.Integration) 
*v1.Integration {
        dst := v1.NewIntegration(o.To, it.Name)
        contImage := it.Status.Image
        dst.Spec = *it.Spec.DeepCopy()
-       if dst.Spec.Traits == nil {
-               dst.Spec.Traits = map[string]v1.TraitSpec{}
+       if dst.Spec.Traits.Container == nil {
+               dst.Spec.Traits.Container = &traitv1.ContainerTrait{}
        }
-       editedContTrait, err := 
editContainerImage(dst.Spec.Traits["container"], contImage)
-       dst.Spec.Traits["container"] = editedContTrait
-       return &dst, err
+       dst.Spec.Traits.Container.Image = contImage
+       return &dst
 }
 
-func (o *promoteCmdOptions) editKameletBinding(kb *v1alpha1.KameletBinding, it 
*v1.Integration) (*v1alpha1.KameletBinding, error) {
+func (o *promoteCmdOptions) editKameletBinding(kb *v1alpha1.KameletBinding, it 
*v1.Integration) *v1alpha1.KameletBinding {
        dst := v1alpha1.NewKameletBinding(o.To, kb.Name)
        dst.Spec = *kb.Spec.DeepCopy()
        contImage := it.Status.Image
        if dst.Spec.Integration == nil {
                dst.Spec.Integration = &v1.IntegrationSpec{}
        }
-       if dst.Spec.Integration.Traits == nil {
-               dst.Spec.Integration.Traits = map[string]v1.TraitSpec{}
+       if dst.Spec.Integration.Traits.Container == nil {
+               dst.Spec.Integration.Traits.Container = 
&traitv1.ContainerTrait{}
        }
-       editedContTrait, err := 
editContainerImage(dst.Spec.Integration.Traits["container"], contImage)
-       dst.Spec.Integration.Traits["container"] = editedContTrait
+       dst.Spec.Integration.Traits.Container.Image = contImage
        if dst.Spec.Source.Ref != nil {
                dst.Spec.Source.Ref.Namespace = o.To
        }
@@ -428,32 +413,7 @@ func (o *promoteCmdOptions) editKameletBinding(kb 
*v1alpha1.KameletBinding, it *
                        }
                }
        }
-       return &dst, err
-}
-
-func editContainerImage(contTrait v1.TraitSpec, image string) (v1.TraitSpec, 
error) {
-       var editedTrait v1.TraitSpec
-       m := make(map[string]map[string]interface{})
-       data, err := json.Marshal(contTrait)
-       if err != nil {
-               return editedTrait, err
-       }
-       err = json.Unmarshal(data, &m)
-       if err != nil {
-               return editedTrait, err
-       }
-       // We must initialize, if it was not initialized so far
-       if m["configuration"] == nil {
-               m["configuration"] = make(map[string]interface{})
-       }
-       m["configuration"]["image"] = image
-       newData, err := json.Marshal(m)
-       if err != nil {
-               return editedTrait, err
-       }
-       err = json.Unmarshal(newData, &editedTrait)
-
-       return editedTrait, err
+       return &dst
 }
 
 //
diff --git a/pkg/cmd/promote_test.go b/pkg/cmd/promote_test.go
deleted file mode 100644
index 3fe2331b5..000000000
--- a/pkg/cmd/promote_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-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 cmd
-
-import (
-       "encoding/json"
-       "testing"
-
-       v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
-       "github.com/stretchr/testify/assert"
-)
-
-func TestEditContainerTrait(t *testing.T) {
-       var containerTrait v1.TraitSpec
-       m := make(map[string]interface{})
-       m["configuration"] = map[string]interface{}{
-               "name":  "myName",
-               "image": "myImage",
-       }
-       data, _ := json.Marshal(m)
-       _ = json.Unmarshal(data, &containerTrait)
-
-       editedContainerTrait, err := editContainerImage(containerTrait, 
"editedImage")
-       assert.Nil(t, err)
-
-       mappedTrait := make(map[string]map[string]interface{})
-       newData, _ := json.Marshal(editedContainerTrait)
-       _ = json.Unmarshal(newData, &mappedTrait)
-
-       assert.Equal(t, "myName", mappedTrait["configuration"]["name"])
-       assert.Equal(t, "editedImage", mappedTrait["configuration"]["image"])
-}
-
-func TestEditMissingContainerTrait(t *testing.T) {
-       var containerTrait v1.TraitSpec
-
-       editedContainerTrait, err := editContainerImage(containerTrait, 
"editedImage")
-       assert.Nil(t, err)
-
-       mappedTrait := make(map[string]map[string]interface{})
-       newData, _ := json.Marshal(editedContainerTrait)
-       _ = json.Unmarshal(newData, &mappedTrait)
-
-       assert.Equal(t, "editedImage", mappedTrait["configuration"]["image"])
-}
diff --git a/pkg/trait/test_support.go b/pkg/trait/test_support.go
index 31d77adc6..5dd0aafb9 100644
--- a/pkg/trait/test_support.go
+++ b/pkg/trait/test_support.go
@@ -18,6 +18,9 @@ limitations under the License.
 package trait
 
 import (
+       "encoding/json"
+       "testing"
+
        serving "knative.dev/serving/pkg/apis/serving/v1"
 
        appsv1 "k8s.io/api/apps/v1"
@@ -26,7 +29,10 @@ import (
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       traitv1 "github.com/apache/camel-k/pkg/apis/camel/v1/trait"
        "github.com/apache/camel-k/pkg/util/kubernetes"
+
+       "github.com/stretchr/testify/require"
 )
 
 func createNominalDeploymentTraitTest() (*Environment, *appsv1.Deployment) {
@@ -115,3 +121,29 @@ func createNominalCronJobTraitTest() (*Environment, 
*batchv1.CronJob) {
 
        return environment, cronJob
 }
+
+// nolint: staticcheck
+func configurationFromMap(t *testing.T, configMap map[string]interface{}) 
*traitv1.Configuration {
+       t.Helper()
+
+       data, err := json.Marshal(configMap)
+       require.NoError(t, err)
+
+       return &traitv1.Configuration{
+               RawMessage: data,
+       }
+}
+
+func traitToMap(t *testing.T, trait interface{}) map[string]interface{} {
+       t.Helper()
+
+       traitMap := make(map[string]interface{})
+
+       data, err := json.Marshal(trait)
+       require.NoError(t, err)
+
+       err = json.Unmarshal(data, &traitMap)
+       require.NoError(t, err)
+
+       return traitMap
+}
diff --git a/pkg/trait/trait_configure.go b/pkg/trait/trait_configure.go
index fb442b28e..fe0287a69 100644
--- a/pkg/trait/trait_configure.go
+++ b/pkg/trait/trait_configure.go
@@ -30,6 +30,7 @@ import (
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 )
 
+// configure reads trait configurations from environment and applies them to 
catalog.
 func (c *Catalog) configure(env *Environment) error {
        if env.Platform != nil {
                if err := c.configureTraits(env.Platform.Status.Traits); err != 
nil {
@@ -68,7 +69,7 @@ func (c *Catalog) configureTraits(traits interface{}) error {
        for id, trait := range traitsMap {
                t := trait // Avoid G601: Implicit memory aliasing in for loop
                if catTrait := c.GetTrait(id); catTrait != nil {
-                       if err := decodeTraitSpec(&t, catTrait); err != nil {
+                       if err := decodeTrait(t, catTrait, true); err != nil {
                                return err
                        }
                }
@@ -77,13 +78,22 @@ func (c *Catalog) configureTraits(traits interface{}) error 
{
        return nil
 }
 
-func decodeTraitSpec(in interface{}, target interface{}) error {
+func decodeTrait(in map[string]interface{}, target Trait, root bool) error {
+       // decode legacy configuration first if it exists
+       if root && in["configuration"] != nil {
+               if config, ok := in["configuration"].(map[string]interface{}); 
ok {
+                       if err := decodeTrait(config, target, false); err != 
nil {
+                               return err
+                       }
+               }
+       }
+
        data, err := json.Marshal(&in)
        if err != nil {
                return err
        }
 
-       return json.Unmarshal(data, &target)
+       return json.Unmarshal(data, target)
 }
 
 func (c *Catalog) configureTraitsFromAnnotations(annotations 
map[string]string) error {
diff --git a/pkg/trait/trait_configure_test.go 
b/pkg/trait/trait_configure_test.go
index 00c374955..125ddd42b 100644
--- a/pkg/trait/trait_configure_test.go
+++ b/pkg/trait/trait_configure_test.go
@@ -21,6 +21,7 @@ import (
        "testing"
 
        "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
 
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/utils/pointer"
@@ -170,3 +171,28 @@ func TestTraitSplitConfiguration(t *testing.T) {
        assert.NoError(t, c.configure(&env))
        assert.Equal(t, []string{"opt1", "opt2"}, 
c.GetTrait("owner").(*ownerTrait).TargetLabels)
 }
+
+func TestTraitDecode(t *testing.T) {
+       trait := traitToMap(t, traitv1.ContainerTrait{
+               Trait: traitv1.Trait{
+                       Enabled: pointer.Bool(false),
+                       Configuration: configurationFromMap(t, 
map[string]interface{}{
+                               "name": "test-container",
+                               "port": 8081,
+                       }),
+               },
+               Port: 7071,
+               Auto: pointer.Bool(false),
+       })
+
+       target, ok := newContainerTrait().(*containerTrait)
+       require.True(t, ok)
+       err := decodeTrait(trait, target, true)
+       require.NoError(t, err)
+
+       assert.Equal(t, false, pointer.BoolDeref(target.Enabled, true))
+       assert.Equal(t, "test-container", target.Name)
+       // legacy configuration should not override a value in new API field
+       assert.Equal(t, 7071, target.Port)
+       assert.Equal(t, false, pointer.BoolDeref(target.Auto, true))
+}
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index efc7aa9fc..b55b43eba 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -136,24 +136,6 @@ func TestKubernetesTraitsWithWeb(t *testing.T) {
        }))
 }
 
-func TestTraitDecode(t *testing.T) {
-       trait := traitv1.ContainerTrait{
-               Trait: traitv1.Trait{
-                       Enabled: pointer.Bool(false),
-               },
-               Port: 7071,
-               Auto: pointer.Bool(true),
-       }
-
-       ctr, _ := newContainerTrait().(*containerTrait)
-       err := decodeTraitSpec(&trait, ctr)
-
-       assert.Nil(t, err)
-       assert.Equal(t, 7071, ctr.Port)
-       assert.NotNil(t, ctr.Enabled)
-       assert.Equal(t, false, *ctr.Enabled)
-}
-
 func TestTraitHierarchyDecode(t *testing.T) {
        env := createTestEnv(t, v1.IntegrationPlatformClusterOpenShift, "")
 
diff --git a/script/Makefile b/script/Makefile
index d2b1509c5..2ffbee1e2 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -326,6 +326,9 @@ clean:
 
 dep:
        go mod tidy
+       cd pkg/apis/camel && go mod tidy
+       cd pkg/client/camel && go mod tidy
+       cd pkg/kamelet/repository && go mod tidy
 
 lint:
        GOGC=$(LINT_GOGC) golangci-lint run --config .golangci.yml --out-format 
tab --deadline $(LINT_DEADLINE) --verbose

Reply via email to