Repository: apache/camel-k
Pull request #4202 (2023-03-30 13:17:52)

[Backport 1.12.x] fix(cmd): run does not expand camel properties
<!-- 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
fix(cmd): run does not expand camel properties
```



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0de8a0114..6d32e39c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,11 +11,13 @@
 - Makefile not working on Mac [\#4175](https://github.com/apache/camel-k/issues/4175)
 - Kamelet development using Camel DSL instead of YAML DSL [\#4174](https://github.com/apache/camel-k/issues/4174)
 - Default integrationPlatform created at operator startup dose not honor OPERATOR\_ID config [\#4167](https://github.com/apache/camel-k/issues/4167)
+- property with dollar sign and brackets replaced with emtpy value [\#4163](https://github.com/apache/camel-k/issues/4163)
 - Kamel cli reset/uninstall --all does not clean the camelcatalog [\#4160](https://github.com/apache/camel-k/issues/4160)
 - Unused parameter [\#4152](https://github.com/apache/camel-k/issues/4152)
 - + [\#4151](https://github.com/apache/camel-k/issues/4151)
 - Kamel run sample integration is failing to run [\#4150](https://github.com/apache/camel-k/issues/4150)
 - Bring an option to force an image build to the kamel run command [\#4126](https://github.com/apache/camel-k/issues/4126)
+- How to use JVM classpaths in Native mode? [\#4124](https://github.com/apache/camel-k/issues/4124)
 - Move from release nightly to Continuous Delivery [\#3260](https://github.com/apache/camel-k/issues/3260)
 
 ## [2.0.0-nightly](https://github.com/apache/camel-k/tree/2.0.0-nightly) (2023-03-22)
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 2199275a0..0d0dbd2c0 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -700,11 +700,11 @@ func (o *runCmdOptions) convertOptionsToTraits(cmd *cobra.Command, c client.Clie
 		return err
 	}
 
-	if err := o.applyProperties(c); err != nil {
+	if err := o.applyProperties(c, o.Properties, "camel.properties"); err != nil {
 		return err
 	}
 
-	if err := o.applyBuildProperties(c); err != nil {
+	if err := o.applyProperties(c, o.BuildProperties, "builder.properties"); err != nil {
 		return err
 	}
 
@@ -744,14 +744,18 @@ func convertToTrait(value, traitParameter string) string {
 	return fmt.Sprintf("%s=%s", traitParameter, value)
 }
 
-func (o *runCmdOptions) applyProperties(c client.Client) error {
-	props, err := o.mergePropertiesWithPrecedence(c, o.Properties)
+func (o *runCmdOptions) applyProperties(c client.Client, items []string, traitName string) error {
+	if len(items) == 0 {
+		return nil
+	}
+	props, err := o.mergePropertiesWithPrecedence(c, items)
 	if err != nil {
 		return err
 	}
 	for _, key := range props.Keys() {
-		kv := fmt.Sprintf("%s=%s", key, props.GetString(key, ""))
-		propsTraits, err := o.convertToTraitParameter(c, kv, "camel.properties")
+		val, _ := props.Get(key)
+		kv := fmt.Sprintf("%s=%s", key, val)
+		propsTraits, err := o.convertToTraitParameter(c, kv, traitName)
 		if err != nil {
 			return err
 		}
@@ -761,30 +765,13 @@ func (o *runCmdOptions) applyProperties(c client.Client) error {
 	return nil
 }
 
-func (o *runCmdOptions) applyBuildProperties(c client.Client) error {
-	// convert each build configuration to a builder trait property
-	buildProps, err := o.mergePropertiesWithPrecedence(c, o.BuildProperties)
-	if err != nil {
-		return err
-	}
-	for _, key := range buildProps.Keys() {
-		kv := fmt.Sprintf("%s=%s", key, buildProps.GetString(key, ""))
-		buildPropsTraits, err := o.convertToTraitParameter(c, kv, "builder.properties")
-		if err != nil {
-			return err
-		}
-		o.Traits = append(o.Traits, buildPropsTraits...)
-	}
-
-	return nil
-}
-
 func (o *runCmdOptions) convertToTraitParameter(c client.Client, value, traitParameter string) ([]string, error) {
 	traits := make([]string, 0)
 	props, err := o.extractProperties(c, value)
 	if err != nil {
 		return nil, err
 	}
+	props.DisableExpansion = true
 	for _, k := range props.Keys() {
 		v, ok := props.Get(k)
 		if ok {
@@ -872,12 +859,15 @@ func (o *runCmdOptions) GetIntegrationName(sources []string) string {
 
 func (o *runCmdOptions) mergePropertiesWithPrecedence(c client.Client, items []string) (*properties.Properties, error) {
 	loPrecedenceProps := properties.NewProperties()
+	loPrecedenceProps.DisableExpansion = true
 	hiPrecedenceProps := properties.NewProperties()
+	hiPrecedenceProps.DisableExpansion = true
 	for _, item := range items {
 		prop, err := o.extractProperties(c, item)
 		if err != nil {
 			return nil, err
 		}
+		prop.DisableExpansion = true
 		// We consider file, secret and config map props to have a lower priority versus single properties
 		if strings.HasPrefix(item, "file:") || strings.HasPrefix(item, "secret:") || strings.HasPrefix(item, "configmap:") {
 			loPrecedenceProps.Merge(prop)


Test patch:
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index 80598d77b..789b039c3 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -710,3 +710,67 @@ func TestIntegrationServiceAccountName(t *testing.T) {
 	assert.Nil(t, err)
 	assert.Contains(t, output, "serviceAccountName: my-service-account")
 }
+
+func TestFileProperties(t *testing.T) {
+	var tmpFile1 *os.File
+	var err error
+	if tmpFile1, err = os.CreateTemp("", "camel-k-*.properties"); err != nil {
+		t.Error(err)
+	}
+
+	assert.Nil(t, tmpFile1.Close())
+	assert.Nil(t, os.WriteFile(tmpFile1.Name(), []byte(`
+	key=${value}
+	#key2=value2
+	my.key=value
+	`), 0o400))
+
+	var tmpFile *os.File
+	if tmpFile, err = os.CreateTemp("", "camel-k-"); err != nil {
+		t.Error(err)
+	}
+
+	assert.Nil(t, tmpFile.Close())
+	assert.Nil(t, os.WriteFile(tmpFile.Name(), []byte(TestSrcContent), 0o400))
+	_, runCmd, _ := initializeRunCmdOptionsWithOutput(t)
+	output, err := test.ExecuteCommand(runCmd, cmdRun, tmpFile.Name(),
+		"-p", "file:"+tmpFile1.Name(),
+		"-o", "yaml",
+	)
+	assert.Nil(t, err)
+	assert.NotContains(t, output, "#key2")
+	assert.Contains(t, output, "my.key = value")
+	assert.Contains(t, output, "key = ${value}")
+}
+
+func TestPropertyShouldNotExpand(t *testing.T) {
+	var tmpFile1 *os.File
+	var err error
+	if tmpFile1, err = os.CreateTemp("", "camel-k-*.properties"); err != nil {
+		t.Error(err)
+	}
+
+	assert.Nil(t, tmpFile1.Close())
+	assert.Nil(t, os.WriteFile(tmpFile1.Name(), []byte(`
+	key=${value}
+	`), 0o400))
+
+	var tmpFile *os.File
+	if tmpFile, err = os.CreateTemp("", "camel-k-"); err != nil {
+		t.Error(err)
+	}
+
+	assert.Nil(t, tmpFile.Close())
+	assert.Nil(t, os.WriteFile(tmpFile.Name(), []byte(TestSrcContent), 0o400))
+	_, runCmd, _ := initializeRunCmdOptionsWithOutput(t)
+	output, err := test.ExecuteCommand(runCmd, cmdRun, tmpFile.Name(),
+		"-o", "yaml",
+		"-p", "runtimeProp=${value}",
+		"--build-property", "buildProp=${value}",
+		"-p", "file:"+tmpFile1.Name(),
+	)
+	assert.Nil(t, err)
+	assert.Contains(t, output, "runtimeProp = ${value}")
+	assert.Contains(t, output, "buildProp = ${value}")
+	assert.Contains(t, output, "key = ${value}")
+}