Repository: apache/camel-k
Pull request #2329 (2021-05-26 14:00:34)

feat(cmd/run): property configuration refactoring
<!-- Description -->

With this PR we finalize the refactoring of build and runtime configuration properties as described in #2003 :

* `build-property` file support (ie `kamel run --build-property file:conf.properties ...`)
* new property file format as build-property (ie `kamel run --property file:conf.properties ...`)
* deprecation of `--property-file` in favor of `--property file:conf.properties`
* modeline support for `--property` and `--build-property` flag
* E2E test for all the supported properties file flags
* Full set of examples for all properties flags (also with modeline enabled)

The original issue was mentioning the creation of a `--config-property` flag. However, I think it's better to keep the usage of `--property` which is already widely used and quite meaningful among developers.

<!--
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/run): property configuration refactoring
```



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index acb93c672..81292053d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 **Closed issues:**
 
 - Timeout when debugging the operator locally [\#2315](https://github.com/apache/camel-k/issues/2315)
+- Creating an IntegrationPlatform with the route Trait Crashes the Operator [\#2313](https://github.com/apache/camel-k/issues/2313)
 - 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)
@@ -16,10 +17,13 @@
 - 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)
+- Add a flag to enable/disable the installation of default kamelets [\#2237](https://github.com/apache/camel-k/issues/2237)
 - Update run-from-github page to correct github example path [\#2230](https://github.com/apache/camel-k/issues/2230)
 - Release 1.4.0 [\#2134](https://github.com/apache/camel-k/issues/2134)
 - Dependency autoloading is not working correctly with YAML format [\#2098](https://github.com/apache/camel-k/issues/2098)
 - PodSpecable Integration [\#2096](https://github.com/apache/camel-k/issues/2096)
+- api: provide java bindings [\#2082](https://github.com/apache/camel-k/issues/2082)
+- Kamelet file parameters [\#2080](https://github.com/apache/camel-k/issues/2080)
 - Can't mount secrets with binary data [\#2009](https://github.com/apache/camel-k/issues/2009)
 - kamelet-binding : suport for error handling [\#1941](https://github.com/apache/camel-k/issues/1941)
 - builder: use structured logging for maven output [\#1783](https://github.com/apache/camel-k/issues/1783)
@@ -33,11 +37,14 @@
 
 - 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))
+- Fix Route Trait Crash [\#2323](https://github.com/apache/camel-k/pull/2323) ([Kyle-Cooley](https://github.com/Kyle-Cooley))
 - 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))
+- feat\(cmd\): build-property flag [\#2309](https://github.com/apache/camel-k/pull/2309) ([squakez](https://github.com/squakez))
+- Fix \#2237: add flag to disable installation of default kamelets [\#2308](https://github.com/apache/camel-k/pull/2308) ([nicolaferraro](https://github.com/nicolaferraro))
 - 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))
diff --git a/docs/modules/ROOT/pages/cli/modeline.adoc b/docs/modules/ROOT/pages/cli/modeline.adoc
index c0f0ec6a2..243c795bc 100644
--- a/docs/modules/ROOT/pages/cli/modeline.adoc
+++ b/docs/modules/ROOT/pages/cli/modeline.adoc
@@ -66,6 +66,9 @@ The following is a partial list of useful options:
 |===
 |Option | Description
 
+|build-property
+|Add a build time property or properties file (syntax, my-key=my-value \| file:/path/to/my-conf.properties)"
+
 |dependency
 |An external library that should be included, e.g. for Maven dependencies `dependency=mvn:org.my:app:1.0`
 
@@ -85,10 +88,7 @@ The following is a partial list of useful options:
 |Trait profile used for deployment
 
 |property
-|Add a camel property
-
-|property-file
-|Bind a property file to the integration, e.g. `property-file=integration.properties`
+|Add a runtime property or properties file (syntax, my-key=my-value \| file:/path/to/my-conf.properties)
 
 |resource
 |Add a resource
diff --git a/examples/modeline/modeline-build-property-file-route.groovy b/examples/modeline/modeline-build-property-file-route.groovy
new file mode 100644
index 000000000..68922359b
--- /dev/null
+++ b/examples/modeline/modeline-build-property-file-route.groovy
@@ -0,0 +1,31 @@
+// 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 file
+//
+// To run this integrations use:
+//
+// kamel run modeline-build-property-file-route.groovy --dev
+//
+
+// camel-k: build-property=file:quarkus.properties
+
+from('timer:build-property')
+    .routeId('build-property')
+    .log('The application name: {{quarkus.application.name}}')
diff --git a/examples/modeline/modeline-build-property-route.groovy b/examples/modeline/modeline-build-property-route.groovy
new file mode 100644
index 000000000..749ad7673
--- /dev/null
+++ b/examples/modeline/modeline-build-property-route.groovy
@@ -0,0 +1,31 @@
+// 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 modeline-build-property-route.groovy --dev
+//
+
+// camel-k: build-property=quarkus.application.name=my-super-application
+
+from('timer:build-property')
+    .routeId('build-property')
+    .log('The application name: {{quarkus.application.name}}')
diff --git a/examples/modeline/modeline-property-file-route.groovy b/examples/modeline/modeline-property-file-route.groovy
new file mode 100644
index 000000000..e831e215b
--- /dev/null
+++ b/examples/modeline/modeline-property-file-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.
+ */
+
+//
+// To run this integrations use:
+//
+// kamel run modeline-property-file-route.groovy --dev
+//
+
+// camel-k: property=file:my.properties
+
+from('timer:property')
+    .routeId('property')
+    .log('property content is: {{my.key.1}} {{my.key.2}}')
diff --git a/examples/modeline/modeline-property-route.groovy b/examples/modeline/modeline-property-route.groovy
new file mode 100644
index 000000000..472096f16
--- /dev/null
+++ b/examples/modeline/modeline-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.
+ */
+
+//
+// To run this integrations use:
+//
+// kamel run modeline-property-route.groovy --dev
+//
+
+// camel-k: property=my.message=test-property
+
+from('timer:property')
+    .routeId('property')
+    .log('property content is: {{my.message}}')
diff --git a/examples/modeline/quarkus.properties b/examples/modeline/quarkus.properties
new file mode 100644
index 000000000..882e2abf4
--- /dev/null
+++ b/examples/modeline/quarkus.properties
@@ -0,0 +1,3 @@
+quarkus.application.name = my-super-application
+quarkus.banner.enabled = true
+#some.other.configuration = commented!
\ No newline at end of file
diff --git a/examples/user-config/build-property-file-route.groovy b/examples/user-config/build-property-file-route.groovy
new file mode 100644
index 000000000..8427b4840
--- /dev/null
+++ b/examples/user-config/build-property-file-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 file
+//
+// To run this integrations use:
+//
+// kamel run --build-property=file:quarkus.properties build-property-file-route.groovy --dev
+//
+
+from('timer:build-property')
+    .routeId('build-property')
+    .log('The application name: {{quarkus.application.name}}')
diff --git a/examples/user-config/property-file-route.groovy b/examples/user-config/property-file-route.groovy
index 13a09fce8..6ce683811 100644
--- a/examples/user-config/property-file-route.groovy
+++ b/examples/user-config/property-file-route.groovy
@@ -19,7 +19,7 @@
 //
 // To run this integrations use:
 //
-// kamel run --property-file my.properties property-file-route.groovy --dev
+// kamel run --property file:my.properties property-file-route.groovy --dev
 //
 
 from('timer:property-file')
diff --git a/examples/user-config/quarkus.properties b/examples/user-config/quarkus.properties
new file mode 100644
index 000000000..882e2abf4
--- /dev/null
+++ b/examples/user-config/quarkus.properties
@@ -0,0 +1,3 @@
+quarkus.application.name = my-super-application
+quarkus.banner.enabled = true
+#some.other.configuration = commented!
\ No newline at end of file
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 1c341d9d6..78129a468 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -78,8 +78,8 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions)
 	cmd.Flags().StringArrayP("dependency", "d", nil, "A dependency that should be included, e.g., \"-d camel-mail\" for a Camel component, or \"-d mvn:org.my:app:1.0\" for a Maven dependency")
 	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().StringArrayP("property", "p", nil, "Add a runtime property or properties file (syntax, my-key=my-value | file:/path/to/my-conf.properties)")
+	cmd.Flags().StringArray("build-property", nil, "Add a build time property or properties file (syntax, my-key=my-value | file:/path/to/my-conf.properties)")
 	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")
@@ -96,7 +96,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions)
 	cmd.Flags().StringArray("open-api", nil, "Add an OpenAPI v2 spec")
 	cmd.Flags().StringArrayP("volume", "v", nil, "Mount a volume into the integration container. E.g \"-v pvcname:/container/path\"")
 	cmd.Flags().StringArrayP("env", "e", nil, "Set an environment variable in the integration container. E.g \"-e MY_VAR=my-value\"")
-	cmd.Flags().StringArray("property-file", nil, "Bind a property file to the integration. E.g. \"--property-file integration.properties\"")
+	cmd.Flags().StringArray("property-file", nil, "[Deprecated] Bind a property file to the integration. E.g. \"--property-file integration.properties\"")
 	cmd.Flags().StringArray("label", nil, "Add a label to the integration. E.g. \"--label my.company=hello\"")
 	cmd.Flags().StringArray("source", nil, "Add source file to your integration, this is added to the list of files listed as arguments of the command")
 	cmd.Flags().String("pod-template", "", "The path of the YAML file containing a PodSpec template to be used for the Integration pods")
@@ -136,9 +136,10 @@ type runCmdOptions struct {
 	LoggingLevels   []string `mapstructure:"logging-levels" yaml:",omitempty"`
 	Volumes         []string `mapstructure:"volumes" yaml:",omitempty"`
 	EnvVars         []string `mapstructure:"envs" yaml:",omitempty"`
-	PropertyFiles   []string `mapstructure:"property-files" yaml:",omitempty"`
-	Labels          []string `mapstructure:"labels" yaml:",omitempty"`
-	Sources         []string `mapstructure:"sources" yaml:",omitempty"`
+	// Deprecated: PropertyFiles has been deprecated in 1.5
+	PropertyFiles []string `mapstructure:"property-files" yaml:",omitempty"`
+	Labels        []string `mapstructure:"labels" yaml:",omitempty"`
+	Sources       []string `mapstructure:"sources" yaml:",omitempty"`
 }
 
 func (o *runCmdOptions) decode(cmd *cobra.Command, args []string) error {
@@ -224,11 +225,22 @@ func (o *runCmdOptions) validate() error {
 		}
 	}
 
+	// Deprecation warning
+	if o.PropertyFiles != nil {
+		fmt.Println("Warn: --property-files has been deprecated. You should use --property file:/path/to/conf.properties instead.")
+	}
 	err := validatePropertyFiles(o.PropertyFiles)
 	if err != nil {
 		return err
 	}
 
+	propertyFiles := filterBuildPropertyFiles(o.Properties)
+	propertyFiles = append(propertyFiles, filterBuildPropertyFiles(o.BuildProperties)...)
+	err = validatePropertyFiles(propertyFiles)
+	if err != nil {
+		return err
+	}
+
 	for _, label := range o.Labels {
 		parts := strings.Split(label, "=")
 		if len(parts) != 2 {
@@ -239,6 +251,17 @@ func (o *runCmdOptions) validate() error {
 	return nil
 }
 
+func filterBuildPropertyFiles(maybePropertyFiles []string) []string {
+	var propertyFiles []string
+	for _, maybePropertyFile := range maybePropertyFiles {
+		if strings.HasPrefix(maybePropertyFile, "file:") {
+			propertyFiles = append(propertyFiles, strings.Replace(maybePropertyFile, "file:", "", 1))
+		}
+	}
+
+	return propertyFiles
+}
+
 func (o *runCmdOptions) run(cmd *cobra.Command, args []string) error {
 	c, err := o.GetCmdClient()
 	if err != nil {
@@ -544,17 +567,33 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
 	for _, item := range o.Dependencies {
 		integration.Spec.AddDependency(item)
 	}
-	for _, pf := range o.PropertyFiles {
-		if err := addPropertyFile(pf, &integration.Spec); err != nil {
-			return nil, err
-		}
+	for _, item := range o.PropertyFiles {
+		// Deprecated: making it compatible with newer mechanism
+		o.Properties = append(o.Properties, "file:"+item)
 	}
 	for _, item := range o.Properties {
-		integration.Spec.AddConfiguration("property", item)
+		props, err := extractProperties(item)
+		if err != nil {
+			return nil, err
+		}
+		if err := addIntegrationProperties(props, &integration.Spec); err != nil {
+			return nil, err
+		}
 	}
 	// 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))
+		props, err := extractProperties(item)
+		if err != nil {
+			return nil, err
+		}
+		for _, k := range props.Keys() {
+			v, ok := props.Get(k)
+			if ok {
+				o.Traits = append(o.Traits, fmt.Sprintf("builder.properties=%s", escapePropertyFileItem(k)+"="+escapePropertyFileItem(v)))
+			} else {
+				return nil, err
+			}
+		}
 	}
 	for _, item := range o.LoggingLevels {
 		integration.Spec.AddConfiguration("property", "logging.level."+item)
@@ -642,6 +681,20 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
 	return &integration, nil
 }
 
+// The function parse the value and if it is a file (file:/path/), it will parse as property file
+// otherwise return a single property built from the item passed as `key=value`
+func extractProperties(value string) (*properties.Properties, error) {
+	if !strings.HasPrefix(value, "file:") {
+		return keyValueProps(value)
+	}
+	// we already validated the existence of files during validate()
+	return loadPropertyFile(strings.Replace(value, "file:", "", 1))
+}
+
+func keyValueProps(value string) (*properties.Properties, error) {
+	return properties.Load([]byte(value), properties.UTF8)
+}
+
 func binaryOrTextResource(fileName string, data []byte, contentType string, base64Compression bool) (v1.ResourceSpec, error) {
 	resourceSpec := v1.ResourceSpec{
 		DataSpec: v1.DataSpec{
@@ -710,11 +763,7 @@ func isLocalAndFileExists(fileName string) (bool, error) {
 	return !info.IsDir(), nil
 }
 
-func addPropertyFile(fileName string, spec *v1.IntegrationSpec) error {
-	props, err := loadPropertyFile(fileName)
-	if err != nil {
-		return err
-	}
+func addIntegrationProperties(props *properties.Properties, spec *v1.IntegrationSpec) error {
 	for _, k := range props.Keys() {
 		v, _ := props.Get(k)
 		spec.AddConfiguration(
diff --git a/pkg/cmd/util_dependencies.go b/pkg/cmd/util_dependencies.go
index be0dfb7e3..ac6f159b0 100644
--- a/pkg/cmd/util_dependencies.go
+++ b/pkg/cmd/util_dependencies.go
@@ -362,15 +362,23 @@ func validateIntegrationFiles(args []string) error {
 
 func validatePropertyFiles(propertyFiles []string) error {
 	for _, fileName := range propertyFiles {
-		if !strings.HasSuffix(fileName, ".properties") {
-			return fmt.Errorf("supported property files must have a .properties extension: %s", fileName)
+		if err := validatePropertyFile(fileName); err != nil {
+			return err
 		}
+	}
 
-		if file, err := os.Stat(fileName); err != nil {
-			return errors.Wrapf(err, "unable to access property file %s", fileName)
-		} else if file.IsDir() {
-			return fmt.Errorf("property file %s is a directory", fileName)
-		}
+	return nil
+}
+
+func validatePropertyFile(fileName string) error {
+	if !strings.HasSuffix(fileName, ".properties") {
+		return fmt.Errorf("supported property files must have a .properties extension: %s", fileName)
+	}
+
+	if file, err := os.Stat(fileName); err != nil {
+		return errors.Wrapf(err, "unable to access property file %s", fileName)
+	} else if file.IsDir() {
+		return fmt.Errorf("property file %s is a directory", fileName)
 	}
 
 	return nil
diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go
index f2d0775d7..bc350c5a8 100644
--- a/pkg/trait/builder.go
+++ b/pkg/trait/builder.go
@@ -73,6 +73,7 @@ func (t *builderTrait) Apply(e *Environment) error {
 		if _, err := e.Client.CamelV1().IntegrationKits(e.IntegrationKit.Namespace).UpdateStatus(e.C, e.IntegrationKit, metav1.UpdateOptions{}); err != nil {
 			return err
 		}
+
 		return nil
 	}
 


Test patch:
diff --git a/e2e/common/config/config_test.go b/e2e/common/config/config_test.go
index ece48fa79..8512a468f 100644
--- a/e2e/common/config/config_test.go
+++ b/e2e/common/config/config_test.go
@@ -50,7 +50,7 @@ func TestRunConfigExamples(t *testing.T) {
 		})
 
 		t.Run("Property file", func(t *testing.T) {
-			Expect(Kamel("run", "-n", ns, "./files/property-file-route.groovy", "--property-file", "./files/my.properties").Execute()).To(Succeed())
+			Expect(Kamel("run", "-n", ns, "./files/property-file-route.groovy", "--property", "file:./files/my.properties").Execute()).To(Succeed())
 			Eventually(IntegrationPodPhase(ns, "property-file-route"), TestTimeoutMedium).Should(Equal(v1.PodRunning))
 			Eventually(IntegrationCondition(ns, "property-file-route", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
 			Eventually(IntegrationLogs(ns, "property-file-route"), TestTimeoutShort).Should(ContainSubstring("hello world"))
@@ -139,5 +139,14 @@ func TestRunConfigExamples(t *testing.T) {
 			Expect(IntegrationKit(ns, "build-property-route")).ShouldNot(Equal(IntegrationKit(ns, "build-property-route-updated")))
 			Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
 		})
+
+		// Build-Properties file
+		t.Run("Build time property file", func(t *testing.T) {
+			Expect(Kamel("run", "-n", ns, "./files/build-property-file-route.groovy", "--build-property", "file:./files/quarkus.properties").Execute()).To(Succeed())
+			Eventually(IntegrationPodPhase(ns, "build-property-file-route"), TestTimeoutMedium).Should(Equal(v1.PodRunning))
+			Eventually(IntegrationCondition(ns, "build-property-file-route", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
+			Eventually(IntegrationLogs(ns, "build-property-file-route"), TestTimeoutShort).Should(ContainSubstring("my-super-application"))
+			Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
+		})
 	})
 }
diff --git a/e2e/common/config/files/build-property-file-route.groovy b/e2e/common/config/files/build-property-file-route.groovy
new file mode 100644
index 000000000..8427b4840
--- /dev/null
+++ b/e2e/common/config/files/build-property-file-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 file
+//
+// To run this integrations use:
+//
+// kamel run --build-property=file:quarkus.properties build-property-file-route.groovy --dev
+//
+
+from('timer:build-property')
+    .routeId('build-property')
+    .log('The application name: {{quarkus.application.name}}')
diff --git a/e2e/common/config/files/quarkus.properties b/e2e/common/config/files/quarkus.properties
new file mode 100644
index 000000000..882e2abf4
--- /dev/null
+++ b/e2e/common/config/files/quarkus.properties
@@ -0,0 +1,3 @@
+quarkus.application.name = my-super-application
+quarkus.banner.enabled = true
+#some.other.configuration = commented!
\ No newline at end of file
diff --git a/pkg/cmd/modeline_test.go b/pkg/cmd/modeline_test.go
index e748e63b9..912e741b2 100644
--- a/pkg/cmd/modeline_test.go
+++ b/pkg/cmd/modeline_test.go
@@ -101,6 +101,28 @@ func TestModelineRunMultipleFiles(t *testing.T) {
 	assert.Equal(t, []string{"run", fileName, fileName2, "--dependency=mvn:org.my:lib1:3.0", "--dependency=mvn:org.my:lib2:3.0"}, flags)
 }
 
+func TestModelineRunProperty(t *testing.T) {
+	dir, err := ioutil.TempDir("", "camel-k-test-")
+	assert.NoError(t, err)
+	defer os.RemoveAll(dir)
+
+	subDir := path.Join(dir, "sub")
+	err = os.Mkdir(subDir, 0777)
+	assert.NoError(t, err)
+
+	file := `
+		// camel-k: property=my-prop=my-val
+	`
+	fileName := path.Join(subDir, "simple.groovy")
+	err = ioutil.WriteFile(fileName, []byte(file), 0777)
+	assert.NoError(t, err)
+
+	cmd, flags, err := NewKamelWithModelineCommand(context.TODO(), []string{"kamel", "run", fileName})
+	assert.NoError(t, err)
+	assert.NotNil(t, cmd)
+	assert.Equal(t, []string{"run", fileName, "--property=my-prop=my-val"}, flags)
+}
+
 func TestModelineRunPropertyFiles(t *testing.T) {
 	dir, err := ioutil.TempDir("", "camel-k-test-")
 	assert.NoError(t, err)
@@ -111,7 +133,58 @@ func TestModelineRunPropertyFiles(t *testing.T) {
 	assert.NoError(t, err)
 
 	file := `
-		// camel-k: property-file=../application.properties
+		// camel-k: property=file:application.properties
+	`
+	fileName := path.Join(subDir, "simple.groovy")
+	err = ioutil.WriteFile(fileName, []byte(file), 0777)
+	assert.NoError(t, err)
+
+	propFile := `
+		a=b
+	`
+	propFileName := path.Join(dir, "application.properties")
+	err = ioutil.WriteFile(propFileName, []byte(propFile), 0777)
+	assert.NoError(t, err)
+
+	cmd, flags, err := NewKamelWithModelineCommand(context.TODO(), []string{"kamel", "run", fileName})
+	assert.NoError(t, err)
+	assert.NotNil(t, cmd)
+	assert.Equal(t, []string{"run", fileName, "--property=file:application.properties"}, flags)
+}
+
+func TestModelineRunBuildProperty(t *testing.T) {
+	dir, err := ioutil.TempDir("", "camel-k-test-")
+	assert.NoError(t, err)
+	defer os.RemoveAll(dir)
+
+	subDir := path.Join(dir, "sub")
+	err = os.Mkdir(subDir, 0777)
+	assert.NoError(t, err)
+
+	file := `
+		// camel-k: build-property=my-build-prop=my-val
+	`
+	fileName := path.Join(subDir, "simple.groovy")
+	err = ioutil.WriteFile(fileName, []byte(file), 0777)
+	assert.NoError(t, err)
+
+	cmd, flags, err := NewKamelWithModelineCommand(context.TODO(), []string{"kamel", "run", fileName})
+	assert.NoError(t, err)
+	assert.NotNil(t, cmd)
+	assert.Equal(t, []string{"run", fileName, "--build-property=my-build-prop=my-val"}, flags)
+}
+
+func TestModelineRunBuildPropertyFiles(t *testing.T) {
+	dir, err := ioutil.TempDir("", "camel-k-test-")
+	assert.NoError(t, err)
+	defer os.RemoveAll(dir)
+
+	subDir := path.Join(dir, "sub")
+	err = os.Mkdir(subDir, 0777)
+	assert.NoError(t, err)
+
+	file := `
+		// camel-k: build-property=file:application.properties
 	`
 	fileName := path.Join(subDir, "simple.groovy")
 	err = ioutil.WriteFile(fileName, []byte(file), 0777)
@@ -127,7 +200,7 @@ func TestModelineRunPropertyFiles(t *testing.T) {
 	cmd, flags, err := NewKamelWithModelineCommand(context.TODO(), []string{"kamel", "run", fileName})
 	assert.NoError(t, err)
 	assert.NotNil(t, cmd)
-	assert.Equal(t, []string{"run", fileName, "--property-file=" + propFileName}, flags)
+	assert.Equal(t, []string{"run", fileName, "--build-property=file:application.properties"}, flags)
 }
 
 func TestModelineInspectSimple(t *testing.T) {
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index f11e15781..1d9192fc6 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -262,7 +262,9 @@ func TestAddPropertyFile(t *testing.T) {
 	assert.Nil(t, ioutil.WriteFile(tmpFile.Name(), []byte(TestPropertyFileContent), 0644))
 
 	spec := v1.IntegrationSpec{}
-	assert.Nil(t, addPropertyFile(tmpFile.Name(), &spec))
+	properties, err := extractProperties("file:" + tmpFile.Name())
+	assert.Nil(t, err)
+	assert.Nil(t, addIntegrationProperties(properties, &spec))
 	assert.Equal(t, 4, len(spec.Configuration))
 	assert.Equal(t, `a=b`, spec.Configuration[0].Value)
 	assert.Equal(t, `c\=d=e`, spec.Configuration[1].Value)
@@ -568,3 +570,46 @@ func TestIsLocalFileAndExists(t *testing.T) {
 	assert.NotNil(t, err)
 	assert.False(t, value)
 }
+
+func TestExtractProperties_SingleKeyValue(t *testing.T) {
+	correctValues := []string{"key=val", "key = val", "key= val", " key   =  val"}
+	for _, val := range correctValues {
+		prop, err := extractProperties(val)
+		assert.Nil(t, err)
+		value, ok := prop.Get("key")
+		assert.True(t, ok)
+		assert.Equal(t, "val", value)
+	}
+}
+
+func TestExtractProperties_FromFile(t *testing.T) {
+	var tmpFile1 *os.File
+	var err error
+	if tmpFile1, err = ioutil.TempFile("", "camel-k-*.properties"); err != nil {
+		t.Error(err)
+	}
+
+	assert.Nil(t, tmpFile1.Close())
+	assert.Nil(t, ioutil.WriteFile(tmpFile1.Name(), []byte(`
+	key=value
+	#key2=value2
+	my.key=value
+	`), 0644))
+
+	props, err := extractProperties("file:" + tmpFile1.Name())
+	assert.Nil(t, err)
+	assert.Equal(t, 2, props.Len())
+	for _, prop := range props.Keys() {
+		value, ok := props.Get(prop)
+		assert.True(t, ok)
+		assert.Equal(t, "value", value)
+	}
+}
+
+func TestFilterBuildPropertyFiles(t *testing.T) {
+	inputValues := []string{"file:/tmp/test", "key=val"}
+	outputValues := filterBuildPropertyFiles(inputValues)
+
+	assert.Equal(t, len(outputValues), 1)
+	assert.Equal(t, outputValues[0], "/tmp/test")
+}
diff --git a/pkg/cmd/util_dependencies_test.go b/pkg/cmd/util_dependencies_test.go
new file mode 100644
index 000000000..0df0cdf45
--- /dev/null
+++ b/pkg/cmd/util_dependencies_test.go
@@ -0,0 +1,68 @@
+/*
+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 (
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestValidatePropertyFiles_ShouldSucceed(t *testing.T) {
+	var tmpFile1 *os.File
+	var err error
+	if tmpFile1, err = ioutil.TempFile("", "camel-k-*.properties"); err != nil {
+		t.Error(err)
+	}
+
+	assert.Nil(t, tmpFile1.Close())
+	assert.Nil(t, ioutil.WriteFile(tmpFile1.Name(), []byte("key=value"), 0644))
+
+	inputValues := []string{tmpFile1.Name()}
+	err = validatePropertyFiles(inputValues)
+
+	assert.Nil(t, err)
+}
+
+func TestValidatePropertyFiles_ShouldFailNotAPropertiesFile(t *testing.T) {
+	var tmpFile1 *os.File
+	var err error
+	if tmpFile1, err = ioutil.TempFile("", "camel-k-"); err != nil {
+		t.Error(err)
+	}
+
+	assert.Nil(t, tmpFile1.Close())
+	assert.Nil(t, ioutil.WriteFile(tmpFile1.Name(), []byte("key=value"), 0644))
+
+	inputValues := []string{tmpFile1.Name()}
+	err = validatePropertyFiles(inputValues)
+
+	assert.NotNil(t, err)
+	assert.True(t, strings.HasPrefix(err.Error(), "supported property files must have a .properties extension"))
+}
+
+func TestValidatePropertyFiles_ShouldFailNotFound(t *testing.T) {
+	inputValues := []string{"/tmp/not-found.properties"}
+	err := validatePropertyFiles(inputValues)
+
+	assert.NotNil(t, err)
+	assert.True(t, strings.HasPrefix(err.Error(), "unable to access property file"))
+}