Repository: apache/camel-k
Pull request #2661 (2021-09-27 16:31:38)

chore: refresh kamelets also when operator version does not change an…
…d do not replace user-defined kamelets

<!-- Description -->




<!--
Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". 

You can (optionally) mark this PR with labels "kind/bug" or "kind/feature" to make sure
the text is added to the right section of the release notes. 
-->

**Release Note**
```release-note
NONE
```



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7fc07ea33..dbd21983d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@
 -  Output redirection Fix: \#2495 [\#2656](https://github.com/apache/camel-k/pull/2656) ([vsokolov00](https://github.com/vsokolov00))
 - chore\(build\): Upgrade Go dependencies [\#2655](https://github.com/apache/camel-k/pull/2655) ([astefanutti](https://github.com/astefanutti))
 - Fix local builds from directory for containerized and non-containerized uses. [\#2654](https://github.com/apache/camel-k/pull/2654) ([doru1004](https://github.com/doru1004))
+- \[TEST\] Global test - support for openshift [\#2653](https://github.com/apache/camel-k/pull/2653) ([bouskaJ](https://github.com/bouskaJ))
 - feat\(trait\): environment variables [\#2650](https://github.com/apache/camel-k/pull/2650) ([squakez](https://github.com/squakez))
 - fix: Switch off color in Maven logs [\#2647](https://github.com/apache/camel-k/pull/2647) ([astefanutti](https://github.com/astefanutti))
 - Alow fixed custom labels in any pod [\#2646](https://github.com/apache/camel-k/pull/2646) ([claudio4j](https://github.com/claudio4j))
@@ -37,11 +38,13 @@
 - Fix \#2638: sync owned integration kit to avoid gc [\#2639](https://github.com/apache/camel-k/pull/2639) ([nicolaferraro](https://github.com/nicolaferraro))
 - Fix documentation for building website [\#2637](https://github.com/apache/camel-k/pull/2637) ([oscerd](https://github.com/oscerd))
 - Fixed website build [\#2636](https://github.com/apache/camel-k/pull/2636) ([oscerd](https://github.com/oscerd))
+- feat\(trait\): camel trait to include properties [\#2635](https://github.com/apache/camel-k/pull/2635) ([squakez](https://github.com/squakez))
 - doc: architecture \> runtime section [\#2634](https://github.com/apache/camel-k/pull/2634) ([squakez](https://github.com/squakez))
 - doc\(native\): Add package type tag to build metrics [\#2632](https://github.com/apache/camel-k/pull/2632) ([astefanutti](https://github.com/astefanutti))
 - chore\(native\): Fail-fast native build for unsupported languages [\#2631](https://github.com/apache/camel-k/pull/2631) ([astefanutti](https://github.com/astefanutti))
 - chore: stale bot configuration [\#2630](https://github.com/apache/camel-k/pull/2630) ([squakez](https://github.com/squakez))
 - fix\(doc\): escape characters in trait doc [\#2628](https://github.com/apache/camel-k/pull/2628) ([squakez](https://github.com/squakez))
+- Upgrade sbo [\#2627](https://github.com/apache/camel-k/pull/2627) ([johnpoth](https://github.com/johnpoth))
 - chore\(release\): Update OLM base CSV manifest [\#2624](https://github.com/apache/camel-k/pull/2624) ([astefanutti](https://github.com/astefanutti))
 - Fix \#2616: correctly encode numbers in Kamelet parameters [\#2617](https://github.com/apache/camel-k/pull/2617) ([nicolaferraro](https://github.com/nicolaferraro))
 - \[website\] fix attribute usage, and put the crd html file in partials [\#2614](https://github.com/apache/camel-k/pull/2614) ([djencks](https://github.com/djencks))
@@ -652,6 +655,7 @@
 - groupId is overriden by `camel-k-integration` using kafka consumer [\#2605](https://github.com/apache/camel-k/issues/2605)
 - Release 1.5.1 [\#2590](https://github.com/apache/camel-k/issues/2590)
 - jitpack dependency is not resolved [\#2589](https://github.com/apache/camel-k/issues/2589)
+- Upgrade Service Binding Operator to latest version [\#2553](https://github.com/apache/camel-k/issues/2553)
 - Evaluate/Cleanup logging trait dependency [\#2541](https://github.com/apache/camel-k/issues/2541)
 - kamelet binding : camel controller fail reconcile the kafka sink resource. [\#2528](https://github.com/apache/camel-k/issues/2528)
 - camel-k-integration override provided groupId name in Kafka component [\#2523](https://github.com/apache/camel-k/issues/2523)
diff --git a/docs/modules/traits/pages/camel.adoc b/docs/modules/traits/pages/camel.adoc
index 59f3db58d..a153a68bb 100755
--- a/docs/modules/traits/pages/camel.adoc
+++ b/docs/modules/traits/pages/camel.adoc
@@ -31,6 +31,10 @@ The following configuration options are available:
 | string
 | The camel-k-runtime version to use for the integration. It overrides the default version set in the Integration Platform.
 
+| camel.properties
+| []string
+| A list of properties to be provided to the Integration runtime
+
 |===
 
 // End of autogenerated code - DO NOT EDIT! (configuration)
diff --git a/docs/modules/traits/pages/istio.adoc b/docs/modules/traits/pages/istio.adoc
index b8fdef066..7fda9136e 100755
--- a/docs/modules/traits/pages/istio.adoc
+++ b/docs/modules/traits/pages/istio.adoc
@@ -1,7 +1,7 @@
 = Istio Trait
 
 // Start of autogenerated code - DO NOT EDIT! (description)
-The Istio trait allows to configure properties related to the Istio service mesh,
+The Istio trait allows configuring properties related to the Istio service mesh,
 such as sidecar injection and outbound IP ranges.
 
 
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 45e1ce2b7..8b48dd927 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -580,8 +580,13 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd *cobra.Command, c client.C
 	if err != nil {
 		return nil, err
 	}
-	if err := addIntegrationProperties(props, &integration.Spec); err != nil {
-		return nil, err
+	for _, key := range props.Keys() {
+		kv := fmt.Sprintf("%s=%s", key, props.GetString(key, ""))
+		if propsTraits, err := convertToTraitParameter(kv, "camel.properties"); err != nil {
+			return nil, err
+		} else {
+			o.Traits = append(o.Traits, propsTraits...)
+		}
 	}
 
 	// convert each build configuration to a builder trait property
@@ -589,16 +594,12 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd *cobra.Command, c client.C
 	if err != nil {
 		return nil, err
 	}
-	for _, k := range buildProps.Keys() {
-		v, ok := buildProps.Get(k)
-		if ok {
-			entry, err := property.EncodePropertyFileEntry(k, v)
-			if err != nil {
-				return nil, err
-			}
-			o.Traits = append(o.Traits, fmt.Sprintf("builder.properties=%s", entry))
-		} else {
+	for _, key := range buildProps.Keys() {
+		kv := fmt.Sprintf("%s=%s", key, buildProps.GetString(key, ""))
+		if buildPropsTraits, err := convertToTraitParameter(kv, "builder.properties"); err != nil {
 			return nil, err
+		} else {
+			o.Traits = append(o.Traits, buildPropsTraits...)
 		}
 	}
 
@@ -686,6 +687,28 @@ func addResource(resourceLocation string, integrationSpec *v1.IntegrationSpec, e
 	return nil
 }
 
+func convertToTraitParameter(value, traitParameter string) ([]string, error) {
+	traits := make([]string, 0)
+	props, err := extractProperties(value)
+	if err != nil {
+		return nil, err
+	}
+	for _, k := range props.Keys() {
+		v, ok := props.Get(k)
+		if ok {
+			entry, err := property.EncodePropertyFileEntry(k, v)
+			if err != nil {
+				return nil, err
+			}
+			traits = append(traits, fmt.Sprintf("%s=%s", traitParameter, entry))
+		} else {
+			return nil, err
+		}
+	}
+
+	return traits, nil
+}
+
 func (o *runCmdOptions) GetIntegrationName(sources []string) string {
 	name := ""
 	if o.IntegrationName != "" {
@@ -713,21 +736,6 @@ func (o *runCmdOptions) configureTraits(integration *v1.Integration, options []s
 	return nil
 }
 
-func addIntegrationProperties(props *properties.Properties, spec *v1.IntegrationSpec) error {
-	for _, k := range props.Keys() {
-		v, _ := props.Get(k)
-		entry, err := property.EncodePropertyFileEntry(k, v)
-		if err != nil {
-			return err
-		}
-		spec.AddConfiguration(
-			"property",
-			entry,
-		)
-	}
-	return nil
-}
-
 func loadPropertyFile(fileName string) (*properties.Properties, error) {
 	file, err := ioutil.ReadFile(fileName)
 	if err != nil {
diff --git a/pkg/install/kamelets.go b/pkg/install/kamelets.go
index f1f7562a8..989dfb059 100644
--- a/pkg/install/kamelets.go
+++ b/pkg/install/kamelets.go
@@ -82,7 +82,7 @@ func KameletCatalog(ctx context.Context, c client.Client, namespace string) erro
 				}
 			}
 
-			if existing == nil || existing.Annotations[kamelVersionAnnotation] != defaults.Version {
+			if existing == nil || existing.Labels[v1alpha1.KameletBundledLabel] == "true" {
 				if k.GetAnnotations() == nil {
 					k.SetAnnotations(make(map[string]string))
 				}
diff --git a/pkg/trait/camel.go b/pkg/trait/camel.go
index 47a66f2c1..726ded4cc 100644
--- a/pkg/trait/camel.go
+++ b/pkg/trait/camel.go
@@ -20,13 +20,18 @@ package trait
 import (
 	"context"
 	"fmt"
+	"strconv"
 	"strings"
 
 	"github.com/pkg/errors"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	ctrl "sigs.k8s.io/controller-runtime/pkg/client"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/maven"
+	"github.com/apache/camel-k/pkg/util/property"
 )
 
 // The Camel trait can be used to configure versions of Apache Camel K runtime and related libraries, it cannot be disabled.
@@ -36,6 +41,8 @@ type camelTrait struct {
 	BaseTrait `property:",squash"`
 	// The camel-k-runtime version to use for the integration. It overrides the default version set in the Integration Platform.
 	RuntimeVersion string `property:"runtime-version" json:"runtimeVersion,omitempty"`
+	// A list of properties to be provided to the Integration runtime
+	Properties []string `property:"properties" json:"properties,omitempty"`
 }
 
 func newCamelTrait() Trait {
@@ -73,6 +80,16 @@ func (t *camelTrait) Apply(e *Environment) error {
 		e.IntegrationKit.Status.RuntimeProvider = e.CamelCatalog.Runtime.Provider
 	}
 
+	if e.IntegrationKitInPhase(v1.IntegrationKitPhaseReady) && e.IntegrationInRunningPhases() {
+		// Get all resources
+		maps := t.computeConfigMaps(e)
+		if t.Properties != nil {
+			// Only user.properties
+			maps = append(maps, t.computeUserProperties(e)...)
+		}
+		e.Resources.AddAll(maps)
+	}
+
 	return nil
 }
 
@@ -152,3 +169,159 @@ func (t *camelTrait) determineRuntimeVersion(e *Environment) string {
 func (t *camelTrait) IsPlatformTrait() bool {
 	return true
 }
+
+func (t *camelTrait) computeConfigMaps(e *Environment) []ctrl.Object {
+	sources := e.Integration.Sources()
+	maps := make([]ctrl.Object, 0, len(sources)+1)
+
+	// combine properties of integration with kit, integration
+	// properties have the priority
+	userProperties := ""
+
+	for _, prop := range e.collectConfigurationPairs("property") {
+		// properties in resource configuration are expected to be pre-encoded using properties format
+		userProperties += fmt.Sprintf("%s=%s\n", prop.Name, prop.Value)
+	}
+
+	if userProperties != "" {
+		maps = append(
+			maps,
+			&corev1.ConfigMap{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ConfigMap",
+					APIVersion: "v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      e.Integration.Name + "-user-properties",
+					Namespace: e.Integration.Namespace,
+					Labels: map[string]string{
+						v1.IntegrationLabel:                e.Integration.Name,
+						"camel.apache.org/properties.type": "user",
+					},
+				},
+				Data: map[string]string{
+					"application.properties": userProperties,
+				},
+			},
+		)
+	}
+
+	for i, s := range sources {
+		if s.ContentRef != "" {
+			continue
+		}
+
+		cm := corev1.ConfigMap{
+			TypeMeta: metav1.TypeMeta{
+				Kind:       "ConfigMap",
+				APIVersion: "v1",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
+				Namespace: e.Integration.Namespace,
+				Labels: map[string]string{
+					v1.IntegrationLabel: e.Integration.Name,
+				},
+				Annotations: map[string]string{
+					"camel.apache.org/source.language":    string(s.InferLanguage()),
+					"camel.apache.org/source.loader":      s.Loader,
+					"camel.apache.org/source.name":        s.Name,
+					"camel.apache.org/source.compression": strconv.FormatBool(s.Compression),
+				},
+			},
+			Data: map[string]string{
+				"content": s.Content,
+			},
+		}
+
+		maps = append(maps, &cm)
+	}
+
+	for i, r := range e.Integration.Spec.Resources {
+		if r.Type == v1.ResourceTypeOpenAPI {
+			continue
+		}
+		if r.ContentRef != "" {
+			continue
+		}
+
+		cmKey := "content"
+		if r.ContentKey != "" {
+			cmKey = r.ContentKey
+		}
+
+		cm := corev1.ConfigMap{
+			TypeMeta: metav1.TypeMeta{
+				Kind:       "ConfigMap",
+				APIVersion: "v1",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      fmt.Sprintf("%s-resource-%03d", e.Integration.Name, i),
+				Namespace: e.Integration.Namespace,
+				Labels: map[string]string{
+					"camel.apache.org/integration": e.Integration.Name,
+				},
+				Annotations: map[string]string{
+					"camel.apache.org/resource.name":        r.Name,
+					"camel.apache.org/resource.compression": strconv.FormatBool(r.Compression),
+				},
+			},
+		}
+
+		if r.ContentType != "" {
+			cm.Annotations["camel.apache.org/resource.content-type"] = r.ContentType
+		}
+
+		if r.RawContent != nil {
+			cm.BinaryData = map[string][]byte{
+				cmKey: r.RawContent,
+			}
+		} else {
+			cm.Data = map[string]string{
+				cmKey: r.Content,
+			}
+		}
+
+		maps = append(maps, &cm)
+	}
+
+	return maps
+}
+
+func (t *camelTrait) computeUserProperties(e *Environment) []ctrl.Object {
+	maps := make([]ctrl.Object, 0)
+
+	// combine properties of integration with kit, integration
+	// properties have the priority
+	userProperties := ""
+
+	for _, prop := range t.Properties {
+		k, v := property.SplitPropertyFileEntry(prop)
+		userProperties += fmt.Sprintf("%s=%s\n", k, v)
+	}
+
+	if userProperties != "" {
+		maps = append(
+			maps,
+			&corev1.ConfigMap{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ConfigMap",
+					APIVersion: "v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      e.Integration.Name + "-user-properties",
+					Namespace: e.Integration.Namespace,
+					Labels: map[string]string{
+						v1.IntegrationLabel:                e.Integration.Name,
+						"camel.apache.org/properties.type": "user",
+					},
+				},
+				Data: map[string]string{
+					"application.properties": userProperties,
+				},
+			},
+		)
+	}
+
+	return maps
+}
diff --git a/pkg/trait/cron.go b/pkg/trait/cron.go
index 9828a58ec..591758b43 100644
--- a/pkg/trait/cron.go
+++ b/pkg/trait/cron.go
@@ -245,9 +245,6 @@ func (t *cronTrait) Apply(e *Environment) error {
 		e.Interceptors = append(e.Interceptors, "cron")
 
 		cronJob := t.getCronJobFor(e)
-		maps := e.computeConfigMaps()
-
-		e.Resources.AddAll(maps)
 		e.Resources.Add(cronJob)
 
 		e.Integration.Status.SetCondition(
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 997dd33b7..2460a5c51 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -99,10 +99,7 @@ func (t *deploymentTrait) ControllerStrategySelectorOrder() int {
 }
 
 func (t *deploymentTrait) Apply(e *Environment) error {
-	maps := e.computeConfigMaps()
 	deployment := t.getDeploymentFor(e)
-
-	e.Resources.AddAll(maps)
 	e.Resources.Add(deployment)
 
 	e.Integration.Status.SetCondition(
diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go
index e0b5cfe11..34d506353 100644
--- a/pkg/trait/knative_service.go
+++ b/pkg/trait/knative_service.go
@@ -175,9 +175,6 @@ func (t *knativeServiceTrait) Apply(e *Environment) error {
 	if err != nil {
 		return err
 	}
-	maps := e.computeConfigMaps()
-
-	e.Resources.AddAll(maps)
 	e.Resources.Add(ksvc)
 
 	e.Integration.Status.SetCondition(
diff --git a/pkg/trait/trait_register.go b/pkg/trait/trait_register.go
index 3f642d0ca..64e8a3931 100644
--- a/pkg/trait/trait_register.go
+++ b/pkg/trait/trait_register.go
@@ -20,36 +20,37 @@ package trait
 func init() {
 	// List of default trait factories.
 	// Declaration order is not important, but let's keep them sorted for debugging.
-	AddToTraits(newInitTrait)
-	AddToTraits(newPlatformTrait)
+	AddToTraits(newAffinityTrait)
+	AddToTraits(newBuilderTrait)
 	AddToTraits(newCamelTrait)
-	AddToTraits(newOpenAPITrait)
-	AddToTraits(newKnativeTrait)
-	AddToTraits(newKameletsTrait)
-	AddToTraits(newErrorHandlerTrait)
+	AddToTraits(newContainerTrait)
+	AddToTraits(newCronTrait)
 	AddToTraits(newDependenciesTrait)
-	AddToTraits(newBuilderTrait)
-	AddToTraits(newQuarkusTrait)
-	AddToTraits(newEnvironmentTrait)
 	AddToTraits(newDeployerTrait)
-	AddToTraits(newCronTrait)
 	AddToTraits(newDeploymentTrait)
+	AddToTraits(newEnvironmentTrait)
+	AddToTraits(newErrorHandlerTrait)
 	AddToTraits(newGarbageCollectorTrait)
-	AddToTraits(newAffinityTrait)
-	AddToTraits(newTolerationTrait)
-	AddToTraits(newKnativeServiceTrait)
-	AddToTraits(newServiceTrait)
-	AddToTraits(newContainerTrait)
-	AddToTraits(newPullSecretTrait)
+	AddToTraits(newIngressTrait)
+	AddToTraits(newIstioTrait)
 	AddToTraits(newJolokiaTrait)
-	AddToTraits(newPrometheusTrait)
 	AddToTraits(newJvmTrait)
-	AddToTraits(newRouteTrait)
-	AddToTraits(newIstioTrait)
-	AddToTraits(newIngressTrait)
-	AddToTraits(newServiceBindingTrait)
+	AddToTraits(newKameletsTrait)
+	AddToTraits(newKnativeTrait)
+	AddToTraits(newKnativeServiceTrait)
+	AddToTraits(newLoggingTraitTrait)
+	AddToTraits(newInitTrait)
+	AddToTraits(newOpenAPITrait)
 	AddToTraits(newOwnerTrait)
 	AddToTraits(newPdbTrait)
+	AddToTraits(newPlatformTrait)
 	AddToTraits(newPodTrait)
-	AddToTraits(newLoggingTraitTrait)
+	AddToTraits(newPrometheusTrait)
+	AddToTraits(newPullSecretTrait)
+	AddToTraits(newQuarkusTrait)
+	AddToTraits(newRouteTrait)
+	AddToTraits(newServiceTrait)
+	AddToTraits(newServiceBindingTrait)
+	AddToTraits(newTolerationTrait)
+	// ^^ Declaration order is not important, but let's keep them sorted for debugging.
 }
diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go
index f3708c844..b549f134b 100644
--- a/pkg/trait/trait_types.go
+++ b/pkg/trait/trait_types.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 	"path"
 	"sort"
-	"strconv"
 	"strings"
 
 	"github.com/pkg/errors"
@@ -34,8 +33,6 @@ import (
 
 	serving "knative.dev/serving/pkg/apis/serving/v1"
 
-	ctrl "sigs.k8s.io/controller-runtime/pkg/client"
-
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	"github.com/apache/camel-k/pkg/client"
 	"github.com/apache/camel-k/pkg/platform"
@@ -400,124 +397,6 @@ func (e *Environment) computeApplicationProperties() (*corev1.ConfigMap, error)
 	return nil, nil
 }
 
-func (e *Environment) computeConfigMaps() []ctrl.Object {
-	sources := e.Integration.Sources()
-	maps := make([]ctrl.Object, 0, len(sources)+1)
-
-	// combine properties of integration with kit, integration
-	// properties have the priority
-	userProperties := ""
-
-	for _, prop := range e.collectConfigurationPairs("property") {
-		// properties in resource configuration are expected to be pre-encoded using properties format
-		userProperties += fmt.Sprintf("%s=%s\n", prop.Name, prop.Value)
-	}
-
-	if userProperties != "" {
-		maps = append(
-			maps,
-			&corev1.ConfigMap{
-				TypeMeta: metav1.TypeMeta{
-					Kind:       "ConfigMap",
-					APIVersion: "v1",
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      e.Integration.Name + "-user-properties",
-					Namespace: e.Integration.Namespace,
-					Labels: map[string]string{
-						v1.IntegrationLabel:                e.Integration.Name,
-						"camel.apache.org/properties.type": "user",
-					},
-				},
-				Data: map[string]string{
-					"application.properties": userProperties,
-				},
-			},
-		)
-	}
-
-	for i, s := range sources {
-		if s.ContentRef != "" {
-			continue
-		}
-
-		cm := corev1.ConfigMap{
-			TypeMeta: metav1.TypeMeta{
-				Kind:       "ConfigMap",
-				APIVersion: "v1",
-			},
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
-				Namespace: e.Integration.Namespace,
-				Labels: map[string]string{
-					v1.IntegrationLabel: e.Integration.Name,
-				},
-				Annotations: map[string]string{
-					"camel.apache.org/source.language":    string(s.InferLanguage()),
-					"camel.apache.org/source.loader":      s.Loader,
-					"camel.apache.org/source.name":        s.Name,
-					"camel.apache.org/source.compression": strconv.FormatBool(s.Compression),
-				},
-			},
-			Data: map[string]string{
-				"content": s.Content,
-			},
-		}
-
-		maps = append(maps, &cm)
-	}
-
-	for i, r := range e.Integration.Spec.Resources {
-		if r.Type == v1.ResourceTypeOpenAPI {
-			continue
-		}
-		if r.ContentRef != "" {
-			continue
-		}
-
-		cmKey := "content"
-		if r.ContentKey != "" {
-			cmKey = r.ContentKey
-		}
-
-		cm := corev1.ConfigMap{
-			TypeMeta: metav1.TypeMeta{
-				Kind:       "ConfigMap",
-				APIVersion: "v1",
-			},
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      fmt.Sprintf("%s-resource-%03d", e.Integration.Name, i),
-				Namespace: e.Integration.Namespace,
-				Labels: map[string]string{
-					"camel.apache.org/integration": e.Integration.Name,
-				},
-				Annotations: map[string]string{
-					"camel.apache.org/resource.name":        r.Name,
-					"camel.apache.org/resource.compression": strconv.FormatBool(r.Compression),
-				},
-			},
-		}
-
-		if r.ContentType != "" {
-			cm.Annotations["camel.apache.org/resource.content-type"] = r.ContentType
-		}
-
-		if r.RawContent != nil {
-			cm.BinaryData = map[string][]byte{
-				cmKey: r.RawContent,
-			}
-		} else {
-			cm.Data = map[string]string{
-				cmKey: r.Content,
-			}
-		}
-
-		maps = append(maps, &cm)
-	}
-
-	return maps
-}
-
 func (e *Environment) addSourcesProperties() {
 	if e.ApplicationProperties == nil {
 		e.ApplicationProperties = make(map[string]string)
diff --git a/resources/traits.yaml b/resources/traits.yaml
index c422f64b0..36b9031ed 100755
--- a/resources/traits.yaml
+++ b/resources/traits.yaml
@@ -73,6 +73,9 @@ traits:
     type: string
     description: The camel-k-runtime version to use for the integration. It overrides
       the default version set in the Integration Platform.
+  - name: properties
+    type: '[]string'
+    description: A list of properties to be provided to the Integration runtime
 - name: container
   platform: true
   profiles:
@@ -322,7 +325,7 @@ traits:
     description: Can be used to enable or disable a trait. All traits share this common
       property.
   - name: discovery-cache
-    type: github.com/apache/camel-k/pkg/trait.discoveryCacheType
+    type: ./pkg/trait.discoveryCacheType
     description: Discovery client cache to be used, either `disabled`, `disk` or `memory`
       (default `memory`)
 - name: ingress
@@ -351,7 +354,7 @@ traits:
   - Kubernetes
   - Knative
   - OpenShift
-  description: The Istio trait allows to configure properties related to the Istio
+  description: The Istio trait allows configuring properties related to the Istio
     service mesh, such as sidecar injection and outbound IP ranges.
   properties:
   - name: enabled


Test patch:
diff --git a/e2e/common/cli/duplicate_parameters_test.go b/e2e/common/cli/duplicate_parameters_test.go
index d893f0d5e..dbda54936 100644
--- a/e2e/common/cli/duplicate_parameters_test.go
+++ b/e2e/common/cli/duplicate_parameters_test.go
@@ -46,6 +46,6 @@ func TestDuplicateParameters(t *testing.T) {
 	// the command is executed inside GetOutputString function
 	commOutput := GetOutputString(comm)
 
-	outParams := `"traits":{"affinity":{"configuration":{"enabled":true}},"pull-secret":{"configuration":{"enabled":true}},"tracing":{"configuration":{"enabled":true}}},"configuration":[{"type":"property","value":"prop1 = true"},{"type":"property","value":"prop2 = true"},{"type":"property","value":"foo = bar"}]}`
+	outParams := `"traits":{"affinity":{"configuration":{"enabled":true}},"camel":{"configuration":{"properties":["prop1 = true","prop2 = true","foo = bar"]}},"pull-secret":{"configuration":{"enabled":true}},"tracing":{"configuration":{"enabled":true}}}`
 	Expect(commOutput).To(ContainSubstring(outParams))
 }
diff --git a/e2e/common/kamelet_binding_test.go b/e2e/common/kamelet_binding_test.go
index 77b669011..511b251c9 100644
--- a/e2e/common/kamelet_binding_test.go
+++ b/e2e/common/kamelet_binding_test.go
@@ -106,7 +106,7 @@ func CreateLogKamelet(ns string, name string) func() error {
 			Type: "string",
 		},
 	}
-	return CreateKamelet(ns, name, flow, props)
+	return CreateKamelet(ns, name, flow, props, nil)
 }
 
 func CreateErrorProducerKamelet(ns string, name string) func() error {
@@ -137,5 +137,5 @@ func CreateErrorProducerKamelet(ns string, name string) func() error {
 		},
 	}
 
-	return CreateKamelet(ns, name, flow, props)
+	return CreateKamelet(ns, name, flow, props, nil)
 }
diff --git a/e2e/common/kamelet_upgrade_test.go b/e2e/common/kamelet_upgrade_test.go
new file mode 100644
index 000000000..5e7405f36
--- /dev/null
+++ b/e2e/common/kamelet_upgrade_test.go
@@ -0,0 +1,77 @@
+// +build integration
+
+// To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "integration"
+
+/*
+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 common
+
+import (
+	"testing"
+	"time"
+
+	. "github.com/apache/camel-k/e2e/support"
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	. "github.com/onsi/gomega"
+)
+
+const preExistingKameletMarker = "pre-existing-kamelet"
+
+func TestKameletUpgrade(t *testing.T) {
+
+	WithNewTestNamespace(t, func(ns string) {
+		Expect(createOperatorManagedKamelet(ns, "http-sink")()).To(Succeed()) // Going to be replaced
+		Expect(createUserManagedKamelet(ns, "ftp-sink")()).To(Succeed())      // Left intact by the operator
+		// Leverages the fact that the default kamelet catalog contains embedded "http-sink" and "ftp-sink"
+
+		Expect(Kamel("install", "-n", ns).Execute()).To(Succeed())
+
+		Eventually(KameletHasLabel("http-sink", ns, preExistingKameletMarker)).Should(BeFalse())
+		Consistently(KameletHasLabel("ftp-sink", ns, preExistingKameletMarker), 5*time.Second, 1*time.Second).Should(BeTrue())
+
+		// Cleanup
+		Expect(Kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil())
+	})
+}
+
+func createOperatorManagedKamelet(ns string, name string) func() error {
+	flow := map[string]interface{}{
+		"from": map[string]interface{}{
+			"uri": "kamelet:source",
+		},
+	}
+
+	labels := map[string]string{
+		preExistingKameletMarker:     "true",
+		v1alpha1.KameletBundledLabel: "true",
+	}
+	return CreateKamelet(ns, name, flow, nil, labels)
+}
+
+func createUserManagedKamelet(ns string, name string) func() error {
+	flow := map[string]interface{}{
+		"from": map[string]interface{}{
+			"uri": "kamelet:source",
+		},
+	}
+
+	labels := map[string]string{
+		preExistingKameletMarker: "true",
+	}
+	return CreateKamelet(ns, name, flow, nil, labels)
+}
diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go
index 5da717563..e2d1e6879 100644
--- a/e2e/support/test_support.go
+++ b/e2e/support/test_support.go
@@ -1236,6 +1236,19 @@ func Kamelet(name string, ns string) func() *v1alpha1.Kamelet {
 	}
 }
 
+func KameletHasLabel(name string, ns string, label string) func() bool {
+	return func() bool {
+		k := Kamelet(name, ns)()
+		if k == nil {
+			return false
+		}
+		if _, ok := k.Labels[label]; ok {
+			return true
+		}
+		return false
+	}
+}
+
 func ClusterDomainName() (string, error) {
 	dns := configv1.DNS{}
 	key := ctrl.ObjectKey{
@@ -1345,12 +1358,13 @@ func CreateKnativeChannel(ns string, name string) func() error {
 	Kamelets
 */
 
-func CreateKamelet(ns string, name string, flow map[string]interface{}, properties map[string]v1alpha1.JSONSchemaProp) func() error {
+func CreateKamelet(ns string, name string, flow map[string]interface{}, properties map[string]v1alpha1.JSONSchemaProp, labels map[string]string) func() error {
 	return func() error {
 		kamelet := v1alpha1.Kamelet{
 			ObjectMeta: metav1.ObjectMeta{
 				Namespace: ns,
 				Name:      name,
+				Labels:    labels,
 			},
 			Spec: v1alpha1.KameletSpec{
 				Definition: &v1alpha1.JSONSchemaProps{
@@ -1386,7 +1400,7 @@ func CreateTimerKamelet(ns string, name string) func() error {
 		},
 	}
 
-	return CreateKamelet(ns, name, flow, props)
+	return CreateKamelet(ns, name, flow, props, nil)
 }
 
 func BindKameletTo(ns string, name string, from corev1.ObjectReference, to corev1.ObjectReference, sourceProperties map[string]string, sinkProperties map[string]string) func() error {
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index bfe9ea6f0..c89d6f48d 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -252,16 +252,12 @@ func TestAddPropertyFile(t *testing.T) {
 	assert.Nil(t, tmpFile.Close())
 	assert.Nil(t, ioutil.WriteFile(tmpFile.Name(), []byte(TestPropertyFileContent), 0644))
 
-	spec := v1.IntegrationSpec{}
-	properties, err := extractProperties("file:" + tmpFile.Name())
+	properties, err := convertToTraitParameter("file:"+tmpFile.Name(), "trait.properties")
 	assert.Nil(t, err)
-	assert.Nil(t, addIntegrationProperties(properties, &spec))
-	assert.Equal(t, 3, len(spec.Configuration))
-	assert.Equal(t, `a = b`, spec.Configuration[0].Value)
-	//assert.Equal(t, `c\=d = e`, spec.Configuration[1].Value)
-	//assert.Equal(t, `d = c\=e`, spec.Configuration[2].Value)
-	assert.Equal(t, `f = g:h`, spec.Configuration[1].Value)
-	assert.Equal(t, `i = j\nk`, spec.Configuration[2].Value)
+	assert.Equal(t, 3, len(properties))
+	assert.Equal(t, `trait.properties=a = b`, properties[0])
+	assert.Equal(t, `trait.properties=f = g:h`, properties[1])
+	assert.Equal(t, `trait.properties=i = j\nk`, properties[2])
 }
 
 func TestRunPropertyFileFlag(t *testing.T) {
@@ -284,12 +280,10 @@ func TestRunPropertyFileFlag(t *testing.T) {
 }
 
 func TestRunProperty(t *testing.T) {
-	spec := v1.IntegrationSpec{}
-	properties, err := extractProperties(`key=value\nnewline`)
+	properties, err := convertToTraitParameter(`key=value\nnewline`, "trait.properties")
 	assert.Nil(t, err)
-	assert.Nil(t, addIntegrationProperties(properties, &spec))
-	assert.Equal(t, 1, len(spec.Configuration))
-	assert.Equal(t, `key = value\nnewline`, spec.Configuration[0].Value)
+	assert.Equal(t, 1, len(properties))
+	assert.Equal(t, `trait.properties=key = value\nnewline`, properties[0])
 }
 
 func TestRunResourceFlag(t *testing.T) {
diff --git a/pkg/trait/camel_test.go b/pkg/trait/camel_test.go
index 293266028..e773b1027 100644
--- a/pkg/trait/camel_test.go
+++ b/pkg/trait/camel_test.go
@@ -21,10 +21,12 @@ import (
 	"context"
 	"testing"
 
+	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	"github.com/apache/camel-k/pkg/util/camel"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/util/test"
 
 	"github.com/stretchr/testify/assert"
@@ -90,12 +92,42 @@ func createNominalCamelTest() (*camelTrait, *Environment) {
 			ObjectMeta: metav1.ObjectMeta{
 				Namespace: "namespace",
 			},
+			Spec: v1.IntegrationSpec{
+				Traits: make(map[string]v1.TraitSpec),
+			},
 			Status: v1.IntegrationStatus{
 				RuntimeVersion: "0.0.1",
+				Phase:          v1.IntegrationPhaseDeploying,
+			},
+		},
+		IntegrationKit: &v1.IntegrationKit{
+			Status: v1.IntegrationKitStatus{
+				Phase: v1.IntegrationKitPhaseReady,
 			},
 		},
-		IntegrationKit: &v1.IntegrationKit{},
+		Platform: &v1.IntegrationPlatform{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "namespace",
+			},
+		},
+		Resources:             kubernetes.NewCollection(),
+		ApplicationProperties: make(map[string]string),
 	}
 
 	return trait, environment
 }
+
+func TestApplyCamelTraitWithProperties(t *testing.T) {
+	camelTrait, environment := createNominalCamelTest()
+	camelTrait.Properties = []string{"a=b", "c=d"}
+	err := camelTrait.Apply(environment)
+	assert.Nil(t, err)
+
+	userPropertiesCm := environment.Resources.GetConfigMap(func(cm *corev1.ConfigMap) bool {
+		return cm.Labels["camel.apache.org/properties.type"] == "user"
+	})
+	assert.NotNil(t, userPropertiesCm)
+	assert.Equal(t, map[string]string{
+		"application.properties": "a=b\nc=d\n",
+	}, userPropertiesCm.Data)
+}
diff --git a/pkg/trait/deployment_test.go b/pkg/trait/deployment_test.go
index de571ca65..efc8456c9 100644
--- a/pkg/trait/deployment_test.go
+++ b/pkg/trait/deployment_test.go
@@ -102,22 +102,9 @@ func TestApplyDeploymentTraitWhileResolvingKitDoesNotSucceed(t *testing.T) {
 
 func TestApplyDeploymentTraitWhileDeployingIntegrationDoesSucceed(t *testing.T) {
 	deploymentTrait, environment := createNominalDeploymentTest()
-	environment.Integration.Spec.Configuration = append(environment.Integration.Spec.Configuration, v1.ConfigurationSpec{
-		Type:  "property",
-		Value: "a=b",
-	})
-
 	err := deploymentTrait.Apply(environment)
-
 	assert.Nil(t, err)
 
-	assert.NotNil(t, environment.Resources.GetConfigMap(func(cm *corev1.ConfigMap) bool {
-		return cm.Labels["camel.apache.org/properties.type"] == "user"
-	}))
-	assert.Nil(t, environment.Resources.GetConfigMap(func(cm *corev1.ConfigMap) bool {
-		return cm.Labels["camel.apache.org/properties.type"] == "application"
-	}))
-
 	deployment := environment.Resources.GetDeployment(func(deployment *appsv1.Deployment) bool { return true })
 	assert.NotNil(t, deployment)
 	assert.Equal(t, "integration-name", deployment.Name)
@@ -182,6 +169,7 @@ func createNominalDeploymentTest() (*deploymentTrait, *Environment) {
 			},
 			Spec: v1.IntegrationSpec{
 				Replicas: &replicas,
+				Traits:   make(map[string]v1.TraitSpec),
 			},
 			Status: v1.IntegrationStatus{
 				Phase: v1.IntegrationPhaseDeploying,
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index e4238300d..888ed712d 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -277,31 +277,19 @@ func TestConfigureVolumesAndMountsTextResourcesAndProperties(t *testing.T) {
 	vols := make([]corev1.Volume, 0)
 	mnts := make([]corev1.VolumeMount, 0)
 
-	env.Resources.AddAll(env.computeConfigMaps())
 	env.configureVolumesAndMounts(&vols, &mnts)
 
-	assert.Len(t, vols, 8)
-	assert.Len(t, mnts, 8)
+	assert.Len(t, vols, 7)
+	assert.Len(t, mnts, 7)
 
-	v := findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "test-user-properties" })
-	assert.NotNil(t, v)
-	assert.NotNil(t, v.VolumeSource.ConfigMap)
-	assert.Len(t, v.VolumeSource.ConfigMap.Items, 1)
-	assert.Equal(t, "application.properties", v.VolumeSource.ConfigMap.Items[0].Key)
-	assert.Equal(t, "user.properties", v.VolumeSource.ConfigMap.Items[0].Path)
-
-	m := findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name })
-	assert.NotNil(t, m)
-	assert.Equal(t, "/etc/camel/conf.d/user.properties", m.MountPath)
-
-	v = findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm1" })
+	v := findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm1" })
 	assert.NotNil(t, v)
 	assert.NotNil(t, v.VolumeSource.ConfigMap)
 	assert.Len(t, v.VolumeSource.ConfigMap.Items, 1)
 	assert.Equal(t, "my-key1", v.VolumeSource.ConfigMap.Items[0].Key)
 	assert.Equal(t, "res1.txt", v.VolumeSource.ConfigMap.Items[0].Path)
 
-	m = findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == "i-resource-000" })
+	m := findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == "i-resource-000" })
 	assert.NotNil(t, m)
 	assert.Equal(t, "/etc/m1", m.MountPath)
 
@@ -401,7 +389,6 @@ func TestConfigureVolumesAndMountsSources(t *testing.T) {
 	vols := make([]corev1.Volume, 0)
 	mnts := make([]corev1.VolumeMount, 0)
 
-	env.Resources.AddAll(env.computeConfigMaps())
 	env.configureVolumesAndMounts(&vols, &mnts)
 
 	assert.Len(t, vols, 2)
@@ -464,7 +451,6 @@ func TestConfigureVolumesAndMountsBinaryAndTextResources(t *testing.T) {
 	vols := make([]corev1.Volume, 0)
 	mnts := make([]corev1.VolumeMount, 0)
 
-	env.Resources.AddAll(env.computeConfigMaps())
 	env.configureVolumesAndMounts(&vols, &mnts)
 
 	assert.Len(t, vols, 2)
@@ -508,7 +494,8 @@ func TestOnlySomeTraitsInfluenceBuild(t *testing.T) {
 
 func TestOnlySomeTraitsArePlatform(t *testing.T) {
 	c := NewTraitTestCatalog()
-	platformTraits := []string{"builder", "camel", "jvm", "container", "dependencies", "deployer", "deployment", "environment", "error-handler", "kamelets", "openapi", "owner", "platform", "quarkus"}
+	platformTraits := []string{"builder", "camel", "jvm", "runtime", "container", "dependencies", "deployer",
+		"deployment", "environment", "error-handler", "kamelets", "openapi", "owner", "platform", "quarkus"}
 
 	for _, trait := range c.AllTraits() {
 		if trait.IsPlatformTrait() {