Repository: apache/camel-k
Pull request #2309 (2021-05-18 13:34:04)

feat(cmd): build-property flag
<!-- Description -->
With this PR we are introducing a new parameter, `--build-property`, whose goal is to let the user specify any configuration needed at build time. For example:
```
kamel run --build-property=quarkus.application.name=my-super-application ...
```
Will provide the `quarkus.application.name` to the properties expected by the build tasks, setting, in this case, the name of the application as expected by `camel-quarkus`.

Ref #2003

<!--
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
feat(cmd): build-property flag
```



Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index dc0a8c426..9ff81c0f5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -40,7 +40,7 @@ on:
       - 'NOTICE'
 
 concurrency:
-  group: ${{ github.head_ref }}-${{ github.workflow }}
+  group: ${{ github.ref }}-${{ github.workflow }}
   cancel-in-progress: true
 
 jobs:
diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml
index 41c9b5981..db4dc2d84 100644
--- a/.github/workflows/builder.yml
+++ b/.github/workflows/builder.yml
@@ -40,7 +40,7 @@ on:
       - 'NOTICE'
 
 concurrency:
-  group: ${{ github.head_ref }}-${{ github.workflow }}
+  group: ${{ github.ref }}-${{ github.workflow }}
   cancel-in-progress: true
 
 jobs:
@@ -79,7 +79,8 @@ jobs:
     - name: Kubernetes KinD Cluster
       uses: container-tools/kind-action@v1
       with:
-        node_image: kindest/node:v1.20.2
+        version: v0.11.0
+        node_image: kindest/node:v1.21.1@sha256:fae9a58f17f18f06aeac9772ca8b5ac680ebbed985e266f711d936e91d113bad
     - name: Info
       run: |
         kubectl cluster-info
diff --git a/.github/workflows/knative.yml b/.github/workflows/knative.yml
index c9f2a119e..e3b5dfae3 100644
--- a/.github/workflows/knative.yml
+++ b/.github/workflows/knative.yml
@@ -40,7 +40,7 @@ on:
       - 'NOTICE'
 
 concurrency:
-  group: ${{ github.head_ref }}-${{ github.workflow }}
+  group: ${{ github.ref }}-${{ github.workflow }}
   cancel-in-progress: true
 
 jobs:
@@ -78,7 +78,8 @@ jobs:
     - name: Kubernetes KinD Cluster
       uses: container-tools/kind-action@v1
       with:
-        node_image: kindest/node:v1.20.2
+        version: v0.11.0
+        node_image: kindest/node:v1.21.1@sha256:fae9a58f17f18f06aeac9772ca8b5ac680ebbed985e266f711d936e91d113bad
     - name: Info
       run: |
         kubectl version
@@ -197,7 +198,8 @@ jobs:
       - name: Kubernetes KinD Cluster
         uses: container-tools/kind-action@v1
         with:
-          node_image: kindest/node:v1.20.2
+          version: v0.11.0
+          node_image: kindest/node:v1.21.1@sha256:fae9a58f17f18f06aeac9772ca8b5ac680ebbed985e266f711d936e91d113bad
       - name: Info
         run: |
           kubectl version
diff --git a/.github/workflows/kubernetes.yml b/.github/workflows/kubernetes.yml
index 23ee8e85b..e074b617b 100644
--- a/.github/workflows/kubernetes.yml
+++ b/.github/workflows/kubernetes.yml
@@ -40,7 +40,7 @@ on:
       - 'NOTICE'
 
 concurrency:
-  group: ${{ github.head_ref }}-${{ github.workflow }}
+  group: ${{ github.ref }}-${{ github.workflow }}
   cancel-in-progress: true
 
 jobs:
@@ -80,7 +80,8 @@ jobs:
     - name: Kubernetes KinD Cluster
       uses: container-tools/kind-action@v1
       with:
-        node_image: kindest/node:v1.20.2
+        version: v0.11.0
+        node_image: kindest/node:v1.21.1@sha256:fae9a58f17f18f06aeac9772ca8b5ac680ebbed985e266f711d936e91d113bad
     - name: Info
       run: |
         kubectl cluster-info
diff --git a/.github/workflows/local.yml b/.github/workflows/local.yml
index 75c360744..0103a3321 100644
--- a/.github/workflows/local.yml
+++ b/.github/workflows/local.yml
@@ -40,7 +40,7 @@ on:
       - 'NOTICE'
 
 concurrency:
-  group: ${{ github.head_ref }}-${{ github.workflow }}
+  group: ${{ github.ref }}-${{ github.workflow }}
   cancel-in-progress: true
 
 jobs:
diff --git a/.github/workflows/openshift.yml b/.github/workflows/openshift.yml
index 259e21dae..e9acac3f4 100644
--- a/.github/workflows/openshift.yml
+++ b/.github/workflows/openshift.yml
@@ -40,7 +40,7 @@ on:
       - 'NOTICE'
 
 concurrency:
-  group: ${{ github.head_ref }}-${{ github.workflow }}
+  group: ${{ github.ref }}-${{ github.workflow }}
   cancel-in-progress: true
 
 jobs:
diff --git a/.github/workflows/upgrade.yml b/.github/workflows/upgrade.yml
index dd346c1fb..1c6b0fffc 100644
--- a/.github/workflows/upgrade.yml
+++ b/.github/workflows/upgrade.yml
@@ -40,7 +40,7 @@ on:
       - 'NOTICE'
 
 concurrency:
-  group: ${{ github.head_ref }}-${{ github.workflow }}
+  group: ${{ github.ref }}-${{ github.workflow }}
   cancel-in-progress: true
 
 jobs:
@@ -86,7 +86,8 @@ jobs:
     - name: Kubernetes KinD Cluster
       uses: container-tools/kind-action@v1
       with:
-        node_image: kindest/node:v1.20.2
+        version: v0.11.0
+        node_image: kindest/node:v1.21.1@sha256:fae9a58f17f18f06aeac9772ca8b5ac680ebbed985e266f711d936e91d113bad
     - name: Info
       run: |
         kubectl cluster-info
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 24a15f001..32ffc078d 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -28,7 +28,7 @@ on:
       - "release-*"
 
 concurrency:
-  group: ${{ github.head_ref }}-${{ github.workflow }}
+  group: ${{ github.ref }}-${{ github.workflow }}
   cancel-in-progress: true
 
 jobs:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5634ee7c7..acb93c672 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,12 +6,14 @@
 
 **Closed issues:**
 
+- Timeout when debugging the operator locally [\#2315](https://github.com/apache/camel-k/issues/2315)
 - Structured logs: remaining bits [\#2286](https://github.com/apache/camel-k/issues/2286)
 - \[Docs\] Broken links on autogenerated API [\#2280](https://github.com/apache/camel-k/issues/2280)
 - Normalize Spectrum build logs [\#2276](https://github.com/apache/camel-k/issues/2276)
 - Normalize Maven build log [\#2268](https://github.com/apache/camel-k/issues/2268)
 - kamelet-binding: improve conditions [\#2265](https://github.com/apache/camel-k/issues/2265)
 - Consolidate make targets used in Github actions [\#2263](https://github.com/apache/camel-k/issues/2263)
+- Flaky scale integration test or scale intermittent issue [\#2250](https://github.com/apache/camel-k/issues/2250)
 - Camel K and Amazon EKS \(Elastic Kubernetes Service\) [\#2241](https://github.com/apache/camel-k/issues/2241)
 - `kamel` run fails for remote files on windows [\#2239](https://github.com/apache/camel-k/issues/2239)
 - Update run-from-github page to correct github example path [\#2230](https://github.com/apache/camel-k/issues/2230)
@@ -29,6 +31,13 @@
 
 **Merged pull requests:**
 
+- Added K3s to the registry section of navbar [\#2325](https://github.com/apache/camel-k/pull/2325) ([oscerd](https://github.com/oscerd))
+- Nav Bar: Titles fixed [\#2324](https://github.com/apache/camel-k/pull/2324) ([oscerd](https://github.com/oscerd))
+- Updated the nav bar with all the installations [\#2322](https://github.com/apache/camel-k/pull/2322) ([oscerd](https://github.com/oscerd))
+- \[TEST\] Increase timeouts for slow infra [\#2319](https://github.com/apache/camel-k/pull/2319) ([bouskaJ](https://github.com/bouskaJ))
+- Fix \#2315: Add leader-election flag to Operator command [\#2316](https://github.com/apache/camel-k/pull/2316) ([johnpoth](https://github.com/johnpoth))
+- Check if the environment can produce structured Maven logs before parsing them [\#2311](https://github.com/apache/camel-k/pull/2311) ([orpiske](https://github.com/orpiske))
+- chore\(ci\): Use `github.ref` instead of `github.head_ref` as concurrency group key [\#2310](https://github.com/apache/camel-k/pull/2310) ([astefanutti](https://github.com/astefanutti))
 - chore\(ci\): Cancel running workflows when PRs are updated [\#2304](https://github.com/apache/camel-k/pull/2304) ([astefanutti](https://github.com/astefanutti))
 - chore: Make phase structured logging key a string [\#2303](https://github.com/apache/camel-k/pull/2303) ([astefanutti](https://github.com/astefanutti))
 - chore\(cli\): Update CRDs when force installing cluster resources [\#2301](https://github.com/apache/camel-k/pull/2301) ([astefanutti](https://github.com/astefanutti))
@@ -57,6 +66,7 @@
 - Fix typo [\#2254](https://github.com/apache/camel-k/pull/2254) ([apupier](https://github.com/apupier))
 - fix\(api\): Fix error handler types deepcopy generation [\#2253](https://github.com/apache/camel-k/pull/2253) ([astefanutti](https://github.com/astefanutti))
 - Added support for the json log format via the logging trait [\#2252](https://github.com/apache/camel-k/pull/2252) ([orpiske](https://github.com/orpiske))
+- fix\(ci\): Upgrade Kubernetes to version 1.21.1 [\#2251](https://github.com/apache/camel-k/pull/2251) ([astefanutti](https://github.com/astefanutti))
 - refactor\(example\): error handler using a real Kafka topic [\#2248](https://github.com/apache/camel-k/pull/2248) ([squakez](https://github.com/squakez))
 - fix\(source\): yaml route steps [\#2247](https://github.com/apache/camel-k/pull/2247) ([squakez](https://github.com/squakez))
 - Improve readability of error messages in Camel K [\#2245](https://github.com/apache/camel-k/pull/2245) ([orpiske](https://github.com/orpiske))
diff --git a/deploy/traits.yaml b/deploy/traits.yaml
index a62ef6699..aa4636e31 100755
--- a/deploy/traits.yaml
+++ b/deploy/traits.yaml
@@ -53,6 +53,9 @@ traits:
     type: bool
     description: Enable verbose logging on build components that support it (e.g.
       Kaniko build pod).
+  - name: build-time-properties
+    type: '[]string'
+    description: A list of properties to be provided to the build task
 - name: camel
   platform: true
   profiles:
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 3f4963280..75027a45a 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -1,10 +1,12 @@
 * xref:installation/installation.adoc[Installation]
 ** xref:installation/minikube.adoc[Minikube]
 ** xref:installation/minishift.adoc[Minishift]
+** xref:installation/crc.adoc[Red Hat CodeReady Containers (CRC)]
 ** xref:installation/digitalocean.adoc[DigitalOcean Kubernetes Cluster]
 ** xref:installation/gke.adoc[Google Kubernetes Engine (GKE)]
 ** xref:installation/openshift.adoc[OpenShift]
 ** xref:installation/iks.adoc[IBM Kubernetes Service (IKS)]
+** xref:installation/k3s.adoc[K3s]
 ** xref:installation/kind.adoc[Kind]
 ** xref:installation/registry/registry.adoc[Configuring Registry]
 *** xref:installation/registry/digitalocean.adoc[DigitalOcean]
@@ -12,6 +14,7 @@
 *** xref:installation/registry/github.adoc[Github Packages]
 *** xref:installation/registry/gcr.adoc[Gcr.io]
 *** xref:installation/registry/icr.adoc[IBM Container Registry]
+*** xref:installation/registry/k3s.adoc[K3s]
 ** xref:installation/scheduling.adoc[Pod scheduling]
 * xref:running/running.adoc[Running]
 ** xref:running/dev-mode.adoc[Dev Mode]
diff --git a/docs/modules/traits/pages/builder.adoc b/docs/modules/traits/pages/builder.adoc
index 26e5ab3d3..c3f42877f 100755
--- a/docs/modules/traits/pages/builder.adoc
+++ b/docs/modules/traits/pages/builder.adoc
@@ -32,6 +32,10 @@ The following configuration options are available:
 | bool
 | Enable verbose logging on build components that support it (e.g. Kaniko build pod).
 
+| builder.build-time-properties
+| []string
+| A list of properties to be provided to the build task
+
 |===
 
 // End of autogenerated code - DO NOT EDIT! (configuration)
diff --git a/examples/user-config/build-property-route.groovy b/examples/user-config/build-property-route.groovy
new file mode 100644
index 000000000..c32e5e654
--- /dev/null
+++ b/examples/user-config/build-property-route.groovy
@@ -0,0 +1,29 @@
+// camel-k: language=groovy
+/*
+ * 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.
+ */
+
+//
+// Provide a build time configuration property (the application name)
+//
+// To run this integrations use:
+//
+// kamel run --build-property=quarkus.application.name=my-super-application build-property-route.groovy --dev
+//
+
+from('timer:build-property')
+    .routeId('build-property')
+    .log('The application name: {{quarkus.application.name}}')
diff --git a/pkg/cmd/operator.go b/pkg/cmd/operator.go
index f3d758e06..a6af5f4e1 100644
--- a/pkg/cmd/operator.go
+++ b/pkg/cmd/operator.go
@@ -39,6 +39,7 @@ func newCmdOperator() (*cobra.Command, *operatorCmdOptions) {
 
 	cmd.Flags().Int32("health-port", 8081, "The port of the health endpoint")
 	cmd.Flags().Int32("monitoring-port", 8080, "The port of the metrics endpoint")
+	cmd.Flags().Bool("leader-election", true, "Use leader election")
 
 	return &cmd, &options
 }
@@ -46,8 +47,9 @@ func newCmdOperator() (*cobra.Command, *operatorCmdOptions) {
 type operatorCmdOptions struct {
 	HealthPort     int32 `mapstructure:"health-port"`
 	MonitoringPort int32 `mapstructure:"monitoring-port"`
+	LeaderElection bool  `mapstructure:"leader-election"`
 }
 
 func (o *operatorCmdOptions) run(_ *cobra.Command, _ []string) {
-	operator.Run(o.HealthPort, o.MonitoringPort)
+	operator.Run(o.HealthPort, o.MonitoringPort, o.LeaderElection)
 }
diff --git a/pkg/cmd/operator/operator.go b/pkg/cmd/operator/operator.go
index c64f50352..d10bd66ac 100644
--- a/pkg/cmd/operator/operator.go
+++ b/pkg/cmd/operator/operator.go
@@ -65,7 +65,7 @@ func printVersion() {
 }
 
 // Run starts the Camel K operator
-func Run(healthPort, monitoringPort int32) {
+func Run(healthPort, monitoringPort int32, leaderElection bool) {
 	rand.Seed(time.Now().UTC().UnixNano())
 
 	flag.Parse()
@@ -102,8 +102,6 @@ func Run(healthPort, monitoringPort int32) {
 		log.Info("Event broadcasting is disabled because of missing permissions to create Events")
 	}
 
-	leaderElection := true
-
 	operatorNamespace := platform.GetOperatorNamespace()
 	if operatorNamespace == "" {
 		// Fallback to using the watch namespace when the operator is not in-cluster.
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 36418a808..1c341d9d6 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -79,6 +79,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions)
 	cmd.Flags().BoolP("wait", "w", false, "Wait for the integration to be running")
 	cmd.Flags().StringP("kit", "k", "", "The kit used to run the integration")
 	cmd.Flags().StringArrayP("property", "p", nil, "Add a camel property")
+	cmd.Flags().StringArray("build-property", nil, "Add a build time property")
 	cmd.Flags().StringArray("configmap", nil, "Add a ConfigMap")
 	cmd.Flags().StringArray("secret", nil, "Add a Secret")
 	cmd.Flags().StringArray("maven-repository", nil, "Add a maven repository")
@@ -127,6 +128,7 @@ type runCmdOptions struct {
 	OpenAPIs        []string `mapstructure:"open-apis" yaml:",omitempty"`
 	Dependencies    []string `mapstructure:"dependencies" yaml:",omitempty"`
 	Properties      []string `mapstructure:"properties" yaml:",omitempty"`
+	BuildProperties []string `mapstructure:"build-properties" yaml:",omitempty"`
 	ConfigMaps      []string `mapstructure:"configmaps" yaml:",omitempty"`
 	Secrets         []string `mapstructure:"secrets" yaml:",omitempty"`
 	Repositories    []string `mapstructure:"maven-repositories" yaml:",omitempty"`
@@ -505,7 +507,7 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
 		}
 	}
 
-	err = resolvePodTemplate(context.Background(),o.PodTemplate, &integration.Spec)
+	err = resolvePodTemplate(context.Background(), o.PodTemplate, &integration.Spec)
 	if err != nil {
 		return nil, err
 	}
@@ -550,6 +552,10 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
 	for _, item := range o.Properties {
 		integration.Spec.AddConfiguration("property", item)
 	}
+	// convert each build configuration to a builder trait property
+	for _, item := range o.BuildProperties {
+		o.Traits = append(o.Traits, fmt.Sprintf("builder.properties=%s", item))
+	}
 	for _, item := range o.LoggingLevels {
 		integration.Spec.AddConfiguration("property", "logging.level."+item)
 	}
@@ -754,14 +760,14 @@ func resolvePodTemplate(ctx context.Context, templateSrc string, spec *v1.Integr
 	//template is inline
 	templateBytes := []byte(templateSrc)
 
-	jsonTemplate,err:= yaml.ToJSON(templateBytes)
-	if err!= nil {
+	jsonTemplate, err := yaml.ToJSON(templateBytes)
+	if err != nil {
 		jsonTemplate = templateBytes
 	}
 	err = json.Unmarshal(jsonTemplate, &template)
 
 	if err == nil {
-		spec.PodTemplate = &v1.PodSpecTemplate {
+		spec.PodTemplate = &v1.PodSpecTemplate{
 			Spec: template,
 		}
 	}
diff --git a/pkg/controller/integration/util.go b/pkg/controller/integration/util.go
index 986b499e2..2087bd6be 100644
--- a/pkg/controller/integration/util.go
+++ b/pkg/controller/integration/util.go
@@ -122,6 +122,10 @@ func LookupKitForIntegration(ctx context.Context, c k8sclient.Reader, integratio
 
 // HasMatchingTraits compare traits defined on kit against those defined on integration.
 func HasMatchingTraits(kit *v1.IntegrationKit, integration *v1.Integration) (bool, error) {
+	// The kit has no trait, but the integration need some
+	if len(kit.Spec.Traits) == 0 && len(integration.Spec.Traits) > 0 {
+		return false, nil
+	}
 	for name, kitTrait := range kit.Spec.Traits {
 		intTrait, ok := integration.Spec.Traits[name]
 		if !ok {
@@ -153,7 +157,7 @@ func HasMatchingTraits(kit *v1.IntegrationKit, integration *v1.Integration) (boo
 				// in integration trait
 				return false, nil
 			}
-			if iv != cv {
+			if !equal(iv, cv) {
 				// skip it because trait configured on kit has a value that differs from
 				// the one configured on integration
 				return false, nil
@@ -163,3 +167,34 @@ func HasMatchingTraits(kit *v1.IntegrationKit, integration *v1.Integration) (boo
 
 	return true, nil
 }
+
+// We need to try to perform a slice equality in order to prevent a runtime panic
+func equal(a, b interface{}) bool {
+	aSlice, aOk := a.([]interface{})
+	bSlice, bOk := b.([]interface{})
+
+	if aOk && bOk {
+		// Both are slices
+		return sliceEqual(aSlice, bSlice)
+	}
+
+	if aOk || bOk {
+		// One of the 2 is a slice
+		return false
+	}
+
+	// None is a slice
+	return a == b
+}
+
+func sliceEqual(a, b []interface{}) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	for i, v := range a {
+		if v != b[i] {
+			return false
+		}
+	}
+	return true
+}
diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go
index 07d740103..f2d0775d7 100644
--- a/pkg/trait/builder.go
+++ b/pkg/trait/builder.go
@@ -18,10 +18,14 @@ limitations under the License.
 package trait
 
 import (
+	"fmt"
 	"sort"
+	"strings"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	"github.com/apache/camel-k/pkg/builder"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 // The builder trait is internally used to determine the best strategy to
@@ -32,6 +36,8 @@ type builderTrait struct {
 	BaseTrait `property:",squash"`
 	// Enable verbose logging on build components that support it (e.g. Kaniko build pod).
 	Verbose *bool `property:"verbose" json:"verbose,omitempty"`
+	// A list of properties to be provided to the build task
+	Properties []string `property:"properties" json:"properties,omitempty"`
 }
 
 func newBuilderTrait() Trait {
@@ -59,7 +65,16 @@ func (t *builderTrait) Configure(e *Environment) (bool, error) {
 }
 
 func (t *builderTrait) Apply(e *Environment) error {
-	builderTask := t.builderTask(e)
+	builderTask, err := t.builderTask(e)
+	if err != nil {
+		e.IntegrationKit.Status.Phase = v1.IntegrationKitPhaseError
+		e.IntegrationKit.Status.SetCondition("IntegrationKitPropertiesFormatValid", corev1.ConditionFalse,
+			"IntegrationKitPropertiesFormatValid", fmt.Sprintf("One or more properties where not formatted as expected: %s", err.Error()))
+		if _, err := e.Client.CamelV1().IntegrationKits(e.IntegrationKit.Namespace).UpdateStatus(e.C, e.IntegrationKit, metav1.UpdateOptions{}); err != nil {
+			return err
+		}
+		return nil
+	}
 
 	e.BuildTasks = append(e.BuildTasks, v1.Task{Builder: builderTask})
 
@@ -118,7 +133,7 @@ func (t *builderTrait) Apply(e *Environment) error {
 	return nil
 }
 
-func (t *builderTrait) builderTask(e *Environment) *v1.BuilderTask {
+func (t *builderTrait) builderTask(e *Environment) (*v1.BuilderTask, error) {
 	task := &v1.BuilderTask{
 		BaseTask: v1.BaseTask{
 			Name: "builder",
@@ -130,6 +145,22 @@ func (t *builderTrait) builderTask(e *Environment) *v1.BuilderTask {
 		Maven:        e.Platform.Status.Build.Maven,
 	}
 
+	// initialize properties if nil
+	if task.Properties == nil {
+		task.Properties = make(map[string]string)
+	}
+	// User provided build time configuration properties
+	if t.Properties != nil {
+		for _, v := range t.Properties {
+			split := strings.Split(v, "=")
+			if len(split) != 2 {
+				return nil, fmt.Errorf("Build time configuration property must have key=value format, it was %v", v)
+			}
+
+			task.Properties[split[0]] = split[1]
+		}
+	}
+
 	steps := make([]builder.Step, 0)
 	steps = append(steps, builder.DefaultSteps...)
 
@@ -143,7 +174,7 @@ func (t *builderTrait) builderTask(e *Environment) *v1.BuilderTask {
 
 	task.Steps = builder.StepIDsFor(steps...)
 
-	return task
+	return task, nil
 }
 
 func getImageName(e *Environment) string {
diff --git a/pkg/util/maven/maven.go b/pkg/util/maven/maven.go
index 3979799bf..596627f5f 100644
--- a/pkg/util/maven/maven.go
+++ b/pkg/util/maven/maven.go
@@ -183,11 +183,13 @@ func Run(ctx Context) error {
 
 		mavenLog, parseError := ParseLog(line)
 
-		if parseError != nil {
-			// Do not abort the build because parsing failed ... the build may have succeeded
-			Log.Error(parseError, "Unable to parse maven log")
-		} else {
+		if parseError == nil {
 			NormalizeLog(mavenLog)
+		} else {
+			// Why we are ignoring the parsing errors here: there are a few scenarios where this would likely occur.
+			// For example, if something outside of Maven outputs something (i.e.: the JDK, a misbehaved plugin,
+			// etc). The build may still have succeeded, though.
+			NonNormalizedLog(line)
 		}
 	}
 	Log.Debug("Finished parsing Maven output")
diff --git a/pkg/util/maven/maven_log.go b/pkg/util/maven/maven_log.go
index 57647e480..3b2536867 100644
--- a/pkg/util/maven/maven_log.go
+++ b/pkg/util/maven/maven_log.go
@@ -43,6 +43,8 @@ const (
 	FATAL = "FATAL"
 )
 
+var mavenLogger = log.WithName("maven.build")
+
 func ParseLog(line string) (mavenLog MavenLog, error error) {
 	error = json.Unmarshal([]byte(line), &mavenLog)
 
@@ -50,14 +52,16 @@ func ParseLog(line string) (mavenLog MavenLog, error error) {
 }
 
 func NormalizeLog(mavenLog MavenLog) {
-	logger := log.WithName("maven.build")
-
 	switch mavenLog.Level {
 	case DEBUG, TRACE:
-		logger.Debug(mavenLog.Msg)
+		mavenLogger.Debug(mavenLog.Msg)
 	case INFO, WARN:
-		logger.Info(mavenLog.Msg)
+		mavenLogger.Info(mavenLog.Msg)
 	case ERROR, FATAL:
-		logger.Errorf(nil, mavenLog.Msg)
+		mavenLogger.Errorf(nil, mavenLog.Msg)
 	}
 }
+
+func NonNormalizedLog(rawLog string) {
+	mavenLogger.Info(rawLog)
+}


Test patch:
diff --git a/e2e/common/config/config_test.go b/e2e/common/config/config_test.go
index 03a0add95..ece48fa79 100644
--- a/e2e/common/config/config_test.go
+++ b/e2e/common/config/config_test.go
@@ -119,5 +119,25 @@ func TestRunConfigExamples(t *testing.T) {
 			Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
 		})
 
+		// Build-Properties
+		t.Run("Build time property", func(t *testing.T) {
+			Expect(Kamel("run", "-n", ns, "./files/build-property-route.groovy", "--build-property", "quarkus.application.name=my-super-application").Execute()).To(Succeed())
+			Eventually(IntegrationPodPhase(ns, "build-property-route"), TestTimeoutMedium).Should(Equal(v1.PodRunning))
+			Eventually(IntegrationCondition(ns, "build-property-route", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
+			Eventually(IntegrationLogs(ns, "build-property-route"), TestTimeoutShort).Should(ContainSubstring("my-super-application"))
+			// Don't delete - we need it for next test execution
+		})
+
+		// We need to check also that the property (which is available in the IntegrationKit) is correctly replaced and we don't reuse the same kit
+		t.Run("Build time property updated", func(t *testing.T) {
+			Expect(Kamel("run", "-n", ns, "./files/build-property-route.groovy", "--name", "build-property-route-updated",
+				"--build-property", "quarkus.application.name=my-super-application-updated").Execute()).To(Succeed())
+			Eventually(IntegrationPodPhase(ns, "build-property-route-updated"), TestTimeoutMedium).Should(Equal(v1.PodRunning))
+			Eventually(IntegrationCondition(ns, "build-property-route-updated", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
+			Eventually(IntegrationLogs(ns, "build-property-route-updated"), TestTimeoutShort).Should(ContainSubstring("my-super-application-updated"))
+			// Verify the integration kits are different
+			Expect(IntegrationKit(ns, "build-property-route")).ShouldNot(Equal(IntegrationKit(ns, "build-property-route-updated")))
+			Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
+		})
 	})
 }
diff --git a/e2e/common/config/files/build-property-route.groovy b/e2e/common/config/files/build-property-route.groovy
new file mode 100644
index 000000000..c32e5e654
--- /dev/null
+++ b/e2e/common/config/files/build-property-route.groovy
@@ -0,0 +1,29 @@
+// camel-k: language=groovy
+/*
+ * 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.
+ */
+
+//
+// Provide a build time configuration property (the application name)
+//
+// To run this integrations use:
+//
+// kamel run --build-property=quarkus.application.name=my-super-application build-property-route.groovy --dev
+//
+
+from('timer:build-property')
+    .routeId('build-property')
+    .log('The application name: {{quarkus.application.name}}')
diff --git a/e2e/yaks/common/kamelet-binding-broker/yaks-config.yaml b/e2e/yaks/common/kamelet-binding-broker/yaks-config.yaml
index e8414f85c..fbaf5889e 100644
--- a/e2e/yaks/common/kamelet-binding-broker/yaks-config.yaml
+++ b/e2e/yaks/common/kamelet-binding-broker/yaks-config.yaml
@@ -32,5 +32,5 @@ pre:
 
     kubectl apply -f timer-source-binding.yaml -n $YAKS_NAMESPACE
     kubectl apply -f logger-sink-binding.yaml -n $YAKS_NAMESPACE
-    kubectl wait kameletbinding timer-source-binding --for=condition=Ready --timeout=10m -n $YAKS_NAMESPACE
-    kubectl wait kameletbinding logger-sink-binding --for=condition=Ready --timeout=10m -n $YAKS_NAMESPACE
+    kubectl wait kameletbinding timer-source-binding --for=condition=Ready --timeout=15m -n $YAKS_NAMESPACE
+    kubectl wait kameletbinding logger-sink-binding --for=condition=Ready --timeout=15m -n $YAKS_NAMESPACE
diff --git a/e2e/yaks/common/kamelet-binding-http/yaks-config.yaml b/e2e/yaks/common/kamelet-binding-http/yaks-config.yaml
index de3709b89..847d512d3 100644
--- a/e2e/yaks/common/kamelet-binding-http/yaks-config.yaml
+++ b/e2e/yaks/common/kamelet-binding-http/yaks-config.yaml
@@ -27,4 +27,4 @@ pre:
 
     kamel run display.groovy -w -n $YAKS_NAMESPACE
     cat timer-source-binding-display.yaml | sed  's/{namespace}/'"${YAKS_NAMESPACE}"'/' | kubectl apply -n $YAKS_NAMESPACE -f -
-    kubectl wait kameletbinding timer-source-binding-display --for=condition=Ready --timeout=10m -n $YAKS_NAMESPACE
+    kubectl wait kameletbinding timer-source-binding-display --for=condition=Ready --timeout=15m -n $YAKS_NAMESPACE
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index 711bba11e..f11e15781 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -409,6 +409,18 @@ func TestRunVolumeFlagWrongPVCFormat(t *testing.T) {
 	assert.NotNil(t, err)
 }
 
+func TestRunBuildPropertyFlag(t *testing.T) {
+	runCmdOptions, rootCmd, _ := initializeRunCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdRun,
+		"--build-property", "build-prop1=val1",
+		"--build-property", "build-prop2=val2",
+		integrationSource)
+	assert.Nil(t, err)
+	assert.Len(t, runCmdOptions.BuildProperties, 2)
+	assert.Equal(t, "build-prop1=val1", runCmdOptions.BuildProperties[0])
+	assert.Equal(t, "build-prop2=val2", runCmdOptions.BuildProperties[1])
+}
+
 func TestRunValidateArgs(t *testing.T) {
 	runCmdOptions, rootCmd, _ := initializeRunCmdOptions(t)
 	args := []string{}
diff --git a/pkg/controller/integration/util_test.go b/pkg/controller/integration/util_test.go
index d6b813b47..0ec08462e 100644
--- a/pkg/controller/integration/util_test.go
+++ b/pkg/controller/integration/util_test.go
@@ -271,3 +271,89 @@ func TestLookupKitForIntegration_DiscardKitsWithIncompatibleTraits(t *testing.T)
 	assert.NotNil(t, i)
 	assert.Equal(t, "my-kit-4", i.Name)
 }
+
+func TestHasMatchingTraits_KitNoTraitShouldNotBePicked(t *testing.T) {
+	integration := &v1.Integration{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: v1.SchemeGroupVersion.String(),
+			Kind:       v1.IntegrationKind,
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "ns",
+			Name:      "my-integration",
+		},
+		Spec: v1.IntegrationSpec{
+			Traits: map[string]v1.TraitSpec{
+				"builder": test.TraitSpecFromMap(t, map[string]interface{}{
+					"enabled": "true",
+				}),
+			},
+		},
+	}
+
+	integrationKitSpec := &v1.IntegrationKit{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: v1.SchemeGroupVersion.String(),
+			Kind:       v1.IntegrationKitKind,
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "ns",
+			Name:      "my-kit",
+		},
+		Spec: v1.IntegrationKitSpec{
+			Traits: map[string]v1.TraitSpec{},
+		},
+	}
+
+	ok, err := HasMatchingTraits(integrationKitSpec, integration)
+	assert.Nil(t, err)
+	assert.False(t, ok)
+}
+
+func TestHasMatchingTraits_KitSameTraitShouldBePicked(t *testing.T) {
+	integration := &v1.Integration{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: v1.SchemeGroupVersion.String(),
+			Kind:       v1.IntegrationKind,
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "ns",
+			Name:      "my-integration",
+		},
+		Spec: v1.IntegrationSpec{
+			Traits: map[string]v1.TraitSpec{
+				"builder": test.TraitSpecFromMap(t, map[string]interface{}{
+					"enabled": "true",
+					"buildTimeProperties": []string{
+						"build-key1=build-value1",
+					},
+				}),
+			},
+		},
+	}
+
+	integrationKitSpec := &v1.IntegrationKit{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: v1.SchemeGroupVersion.String(),
+			Kind:       v1.IntegrationKitKind,
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "ns",
+			Name:      "my-kit",
+		},
+		Spec: v1.IntegrationKitSpec{
+			Traits: map[string]v1.TraitSpec{
+				"builder": test.TraitSpecFromMap(t, map[string]interface{}{
+					"enabled": "true",
+					"buildTimeProperties": []string{
+						"build-key1=build-value1",
+					},
+				}),
+			},
+		},
+	}
+
+	ok, err := HasMatchingTraits(integrationKitSpec, integration)
+	assert.Nil(t, err)
+	assert.True(t, ok)
+}
diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
index 2bce1b57d..31fea5211 100644
--- a/pkg/trait/builder_test.go
+++ b/pkg/trait/builder_test.go
@@ -27,6 +27,7 @@ import (
 	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"
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/defaults"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
@@ -150,3 +151,21 @@ func createBuilderTestEnv(cluster v1.IntegrationPlatformCluster, strategy v1.Int
 func NewBuilderTestCatalog() *Catalog {
 	return NewCatalog(context.TODO(), nil)
 }
+
+func TestBuildtimeConfigurationBuilderTrait(t *testing.T) {
+	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyKaniko)
+	builderTrait := createNominalBuilderTraitTest()
+	builderTrait.Properties = append(builderTrait.Properties, "build-time-prop1=build-time-value1")
+
+	err := builderTrait.Apply(env)
+
+	assert.Nil(t, err)
+	assert.Equal(t, "build-time-value1", env.BuildTasks[0].Builder.Properties["build-time-prop1"])
+}
+
+func createNominalBuilderTraitTest() *builderTrait {
+	builderTrait := newBuilderTrait().(*builderTrait)
+	builderTrait.Enabled = util.BoolP(true)
+
+	return builderTrait
+}