This is an automated email from the ASF dual-hosted git repository.
nfilotto pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push:
new 6e6aff2e7 feat(cmd/run): secret/configmap as runtime/build-time
properties (#3626)
6e6aff2e7 is described below
commit 6e6aff2e7e72d3b3620adfde1c6d917c5830a693
Author: Nicolas Filotto <[email protected]>
AuthorDate: Tue Sep 13 08:49:50 2022 +0200
feat(cmd/run): secret/configmap as runtime/build-time properties (#3626)
fixes #3002
## Motivation:
It would be nice to provide the possibility to use configmap and secret as
runtime or build-time properties for the camel and builder traits.
## Modifications:
* Add the support of `configmap:name-of-configmap` and
`secret:name-of-secret` to the flags `property` and `build-property`
* Move the functions `extractProperties` and
`mergePropertiesWithPrecedence` from `run_help.go` to `run.go` as we need to
add a receiver
* Move the unit tests of the functions moved to the corresponding test file
* Add related E2E test
* Add related doc
---
.../pages/configuration/build-time-properties.adoc | 62 +++++++++++++-
.../pages/configuration/runtime-properties.adoc | 62 +++++++++++++-
e2e/global/common/config/config_test.go | 95 ++++++++++++++++++++++
pkg/cmd/run.go | 61 +++++++++++---
pkg/cmd/run_help.go | 66 +++++++++------
pkg/cmd/run_help_test.go | 63 --------------
pkg/cmd/run_test.go | 69 +++++++++++++++-
7 files changed, 372 insertions(+), 106 deletions(-)
diff --git a/docs/modules/ROOT/pages/configuration/build-time-properties.adoc
b/docs/modules/ROOT/pages/configuration/build-time-properties.adoc
index 713c3f182..b8d239eb9 100644
--- a/docs/modules/ROOT/pages/configuration/build-time-properties.adoc
+++ b/docs/modules/ROOT/pages/configuration/build-time-properties.adoc
@@ -50,10 +50,70 @@ kamel run --build-property=file:quarkus.properties
build-property-route.groovy
The property file is parsed and its properties configured on the
`Integration`. As soon as the application starts, you will see the log with the
expected configuration.
+[[build-time-configmap]]
+== Property from ConfigMap/Secret
+
+In case some build-time properties are stored into a `Configmap` or a
`Secret`, you can use the `--build-property` flag with a value of type
respectively _configmap:name-of-configmap_ or _secret:name-of-secret_ to refer
to the specific resource to use as build-time properties.
+
+As an example, let's create a `Configmap` named _my-cm-bp_ containing the
build-time properties to load. You can alternatively use any `Configmap` you've
already stored in your cluster:
+
+----
+kubectl create configmap my-cm-bp
--from-literal=quarkus.application.name="my-great-application"
--from-literal=quarkus.banner.enabled="true"
+----
+
+Here, as an example we have create a configmap with 2 `Quarkus` properties.
+
+[source,groovy]
+.build-property-route.groovy
+----
+from('timer:build-property')
+ .log('The application name: {{quarkus.application.name}}')
+----
+
+The `quarkus.banner.enabled` is configured to show the banner during the
`Integration` startup. Let's use `--build-property` flag in conjunction with
file:
+
+----
+kamel run --build-property=configmap:my-cm-bp build-property-route.groovy
+----
+
+The key-value pairs of the `ConfigMap` are loaded and used as build-time
properties of the `Integration`. As soon as the application starts, you will
see the log with the expected configuration.
+
+[[build-time-configmap-as-file]]
+== Property from ConfigMap/Secret as file
+
+When you have a lot of key-value pairs to store into a given
`ConfigMap`/`Secret`, you may consider storing some build-time properties as a
file into a specific key-value pair for the sake of simplicity.
+
+The only constraint is to use `.properties` as a suffix of the key to indicate
that the value is actually a property file, not a simple value.
+
+As an example, let's use the same `Integration` as the previous section but
with a `ConfigMap` that contains all the properties into the same key-value
pair.
+
+For this we need a properties file as next:
+
+[source,properties]
+.quarkus.properties
+----
+quarkus.application.name = my-super-application
+quarkus.banner.enabled = true
+----
+
+That we will load into a specific `ConfigMap` using the following command:
+
+----
+kubectl create configmap my-cm-bps --from-file=quarkus.properties
+----
+
+Then we launch the `run` command with the `--build-property` flag whose value
matches with the appropriate syntax to refer to `my-cm-bps`:
+
+----
+kamel run --build-property configmap:my-cm-bps build-property-route.groovy
+----
+
+The value of the key-value of the `ConfigMap` is loaded as a property file and
used as build-time properties of the `Integration`. you will see the log with
the expected configuration.
+
[[build-time-props-file-precedence]]
== Property collision priority
-If you have a property repeated more than once, the general rule is that the
last one declared in your `kamel run` statement will be taken in consideration.
If the same property is found both in a single option declaration and inside a
file, then, the single option will have higher priority and will be used.
+If you have a property repeated more than once, the general rule is that the
last one declared in your `kamel run` statement will be taken in consideration.
If the same property is found both in a single option declaration and inside a
file/configmap/secret, then, the single option will have higher priority and
will be used.
[[build-time-runtime-conf]]
== Run time properties
diff --git a/docs/modules/ROOT/pages/configuration/runtime-properties.adoc
b/docs/modules/ROOT/pages/configuration/runtime-properties.adoc
index c65b5efb2..6ea961640 100644
--- a/docs/modules/ROOT/pages/configuration/runtime-properties.adoc
+++ b/docs/modules/ROOT/pages/configuration/runtime-properties.adoc
@@ -67,10 +67,70 @@ kamel run --property file:my.properties
property-route.groovy
The property file is parsed and its properties configured on the
`Integration`. As soon as the application starts, you will see the log with the
expected configuration.
+[[runtime-configmap]]
+== Property from ConfigMap/Secret
+
+In case some runtime properties are stored into a `Configmap` or a `Secret`,
you can use the `--property` flag with a value of type respectively
_configmap:name-of-configmap_ or _secret:name-of-secret_ to refer to the
specific resource to use as runtime properties.
+
+As an example, let's create a `Configmap` named _my-cm-rp_ containing the
runtime properties to load. You can alternatively use any `Configmap` you've
already stored in your cluster:
+
+----
+kubectl create configmap my-cm-rp --from-literal=name="Will Smith"
--from-literal=period="2000"
+----
+
+In our `Integration` we can simply refer to the properties defined in the
`ConfigMap` as we'd do with any other property:
+
+[source,groovy]
+.property-configmap-route.groovy
+----
+from('timer:property?period={{period}}')
+ .log('Hello {{name}}!')
+----
+
+Then we launch the `run` command with the `--property` flag whose value
matches with the appropriate syntax to refer to `my-cm-rp`:
+
+----
+kamel run --property configmap:my-cm-rp property-configmap-route.groovy
+----
+
+The key-value pairs of the `ConfigMap` are loaded and used as runtime
properties of the `Integration`. As soon as the application starts, you will
see the log with the expected message.
+
+[[runtime-configmap-as-file]]
+== Property from ConfigMap/Secret as file
+
+When you have a lot of key-value pairs to store into a given
`ConfigMap`/`Secret`, you may consider storing some runtime properties as a
file into a specific key-value pair for the sake of simplicity.
+
+The only constraint is to use `.properties` as a suffix of the key to indicate
that the value is actually a property file, not a simple value.
+
+As an example, let's use the same `Integration` as the previous section but
with a `ConfigMap` that contains all the properties into the same key-value
pair.
+
+For this we need a properties file as next:
+
+[source,text]
+.some.properties
+----
+name=John Smith
+period=2000
+----
+
+That we will load into a specific `ConfigMap` using the following command:
+
+----
+kubectl create configmap my-cm-rps --from-file=some.properties
+----
+
+Then we launch the `run` command with the `--property` flag whose value
matches with the appropriate syntax to refer to `my-cm-rps`:
+
+----
+kamel run --property configmap:my-cm-rps property-configmap-route.groovy
+----
+
+The value of the key-value of the `ConfigMap` is loaded as a property file and
used as runtime properties of the `Integration`. As soon as the application
starts, you will see the log with the expected message.
+
[[runtime-props-file-precedence]]
== Property collision priority
-If you have a property repeated more than once, the general rule is that the
last one declared in your `kamel run` statement will be taken in consideration.
If the same property is found both in a single option declaration and inside a
file, then, the single option will have higher priority and will be used.
+If you have a property repeated more than once, the general rule is that the
last one declared in your `kamel run` statement will be taken in consideration.
If the same property is found both in a single option declaration and inside a
file/configmap/secret, then, the single option will have higher priority and
will be used.
[[runtime-build-time-conf]]
== Build time properties
diff --git a/e2e/global/common/config/config_test.go
b/e2e/global/common/config/config_test.go
index 9363fb391..a525ec273 100644
--- a/e2e/global/common/config/config_test.go
+++ b/e2e/global/common/config/config_test.go
@@ -68,6 +68,54 @@ func TestRunConfigExamples(t *testing.T) {
Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
})
+ t.Run("Property from ConfigMap", func(t *testing.T) {
+ var cmData = make(map[string]string)
+ cmData["my.message"] = "my-configmap-property-value"
+ CreatePlainTextConfigmap(ns, "my-cm-test-property",
cmData)
+
+ Expect(KamelRunWithID(operatorID, ns,
"./files/property-route.groovy", "-p",
"configmap:my-cm-test-property").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, "property-route"),
TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns,
"property-route", v1.IntegrationConditionReady),
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, "property-route"),
TestTimeoutShort).Should(ContainSubstring("my-configmap-property-value"))
+ Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
+ })
+
+ t.Run("Property from ConfigMap as property file", func(t
*testing.T) {
+ var cmData = make(map[string]string)
+ cmData["my.properties"] =
"my.message=my-configmap-property-entry"
+ CreatePlainTextConfigmap(ns, "my-cm-test-properties",
cmData)
+
+ Expect(KamelRunWithID(operatorID, ns,
"./files/property-route.groovy", "-p",
"configmap:my-cm-test-properties").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, "property-route"),
TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns,
"property-route", v1.IntegrationConditionReady),
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, "property-route"),
TestTimeoutShort).Should(ContainSubstring("my-configmap-property-entry"))
+ Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
+ })
+
+ t.Run("Property from Secret", func(t *testing.T) {
+ var secData = make(map[string]string)
+ secData["my.message"] = "my-secret-property-value"
+ CreatePlainTextSecret(ns, "my-sec-test-property",
secData)
+
+ Expect(KamelRunWithID(operatorID, ns,
"./files/property-route.groovy", "-p",
"secret:my-sec-test-property").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, "property-route"),
TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns,
"property-route", v1.IntegrationConditionReady),
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, "property-route"),
TestTimeoutShort).Should(ContainSubstring("my-secret-property-value"))
+ Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
+ })
+
+ t.Run("Property from Secret as property file", func(t
*testing.T) {
+ var secData = make(map[string]string)
+ secData["my.properties"] =
"my.message=my-secret-property-entry"
+ CreatePlainTextSecret(ns, "my-sec-test-properties",
secData)
+
+ Expect(KamelRunWithID(operatorID, ns,
"./files/property-route.groovy", "-p",
"secret:my-sec-test-properties").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, "property-route"),
TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns,
"property-route", v1.IntegrationConditionReady),
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, "property-route"),
TestTimeoutShort).Should(ContainSubstring("my-secret-property-entry"))
+ Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
+ })
+
// Configmap
// Store a configmap on the cluster
@@ -274,5 +322,52 @@ func TestRunConfigExamples(t *testing.T) {
Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
})
+ t.Run("Build time property from ConfigMap", func(t *testing.T) {
+ var cmData = make(map[string]string)
+ cmData["quarkus.application.name"] =
"my-cool-application"
+ CreatePlainTextConfigmap(ns,
"my-cm-test-build-property", cmData)
+
+ Expect(KamelRunWithID(operatorID, ns,
"./files/build-property-file-route.groovy", "--build-property",
"configmap:my-cm-test-build-property").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns,
"build-property-file-route"), TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns,
"build-property-file-route", v1.IntegrationConditionReady),
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns,
"build-property-file-route"),
TestTimeoutShort).Should(ContainSubstring("my-cool-application"))
+ Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
+ })
+
+ t.Run("Build time property from ConfigMap as property file",
func(t *testing.T) {
+ var cmData = make(map[string]string)
+ cmData["my.properties"] =
"quarkus.application.name=my-super-cool-application"
+ CreatePlainTextConfigmap(ns,
"my-cm-test-build-properties", cmData)
+
+ Expect(KamelRunWithID(operatorID, ns,
"./files/build-property-file-route.groovy", "--build-property",
"configmap:my-cm-test-build-properties").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns,
"build-property-file-route"), TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns,
"build-property-file-route", v1.IntegrationConditionReady),
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns,
"build-property-file-route"),
TestTimeoutShort).Should(ContainSubstring("my-super-cool-application"))
+ Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
+ })
+
+ t.Run("Build time property from Secret", func(t *testing.T) {
+ var secData = make(map[string]string)
+ secData["quarkus.application.name"] =
"my-great-application"
+ CreatePlainTextSecret(ns, "my-sec-test-build-property",
secData)
+
+ Expect(KamelRunWithID(operatorID, ns,
"./files/build-property-file-route.groovy", "--build-property",
"secret:my-sec-test-build-property").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns,
"build-property-file-route"), TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns,
"build-property-file-route", v1.IntegrationConditionReady),
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns,
"build-property-file-route"),
TestTimeoutShort).Should(ContainSubstring("my-great-application"))
+ Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
+ })
+
+ t.Run("Build time property from Secret as property file",
func(t *testing.T) {
+ var secData = make(map[string]string)
+ secData["my.properties"] =
"quarkus.application.name=my-awsome-application"
+ CreatePlainTextSecret(ns,
"my-sec-test-build-properties", secData)
+
+ Expect(KamelRunWithID(operatorID, ns,
"./files/build-property-file-route.groovy", "--build-property",
"secret:my-sec-test-build-properties").Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns,
"build-property-file-route"), TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns,
"build-property-file-route", v1.IntegrationConditionReady),
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns,
"build-property-file-route"),
TestTimeoutShort).Should(ContainSubstring("my-awsome-application"))
+ Expect(Kamel("delete", "--all", "-n",
ns).Execute()).To(Succeed())
+ })
})
}
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index ac4174ff4..301aa05b3 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -101,8 +101,8 @@ func newCmdRun(rootCmdOptions *RootCmdOptions)
(*cobra.Command, *runCmdOptions)
cmd.Flags().StringArrayP("dependency", "d", nil, usageDependency)
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 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().StringArrayP("property", "p", nil, "Add a runtime property
or properties file from a path, a config map or a secret (syntax:
[my-key=my-value|file:/path/to/my-conf.properties|[configmap|secret]:name])")
+ cmd.Flags().StringArray("build-property", nil, "Add a build time
property or properties file from a path, a config map or a secret (syntax:
[my-key=my-value|file:/path/to/my-conf.properties|[configmap|secret]:name]])")
cmd.Flags().StringArray("config", nil, "Add a runtime configuration
from a Configmap, a Secret or a file (syntax:
[configmap|secret|file]:name[/key], where name represents the local file path
or the configmap/secret name and key optionally represents the configmap/secret
key to be filtered)")
cmd.Flags().StringArray("resource", nil, "Add a runtime resource from a
Configmap, a Secret or a file (syntax:
[configmap|secret|file]:name[/key][@path], where name represents the local file
path or the configmap/secret name, key optionally represents the
configmap/secret key to be filtered and path represents the destination path)")
cmd.Flags().StringArray("maven-repository", nil, "Add a maven
repository")
@@ -694,11 +694,11 @@ func (o *runCmdOptions) convertOptionsToTraits(cmd
*cobra.Command, c client.Clie
return err
}
- if err := o.applyProperties(); err != nil {
+ if err := o.applyProperties(c); err != nil {
return err
}
- if err := o.applyBuildProperties(); err != nil {
+ if err := o.applyBuildProperties(c); err != nil {
return err
}
@@ -738,14 +738,14 @@ func convertToTrait(value, traitParameter string) string {
return fmt.Sprintf("%s=%s", traitParameter, value)
}
-func (o *runCmdOptions) applyProperties() error {
- props, err := mergePropertiesWithPrecedence(o.Properties)
+func (o *runCmdOptions) applyProperties(c client.Client) error {
+ props, err := o.mergePropertiesWithPrecedence(c, o.Properties)
if err != nil {
return err
}
for _, key := range props.Keys() {
kv := fmt.Sprintf("%s=%s", key, props.GetString(key, ""))
- propsTraits, err := convertToTraitParameter(kv,
"camel.properties")
+ propsTraits, err := o.convertToTraitParameter(c, kv,
"camel.properties")
if err != nil {
return err
}
@@ -755,15 +755,15 @@ func (o *runCmdOptions) applyProperties() error {
return nil
}
-func (o *runCmdOptions) applyBuildProperties() error {
+func (o *runCmdOptions) applyBuildProperties(c client.Client) error {
// convert each build configuration to a builder trait property
- buildProps, err := mergePropertiesWithPrecedence(o.BuildProperties)
+ 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 := convertToTraitParameter(kv,
"builder.properties")
+ buildPropsTraits, err := o.convertToTraitParameter(c, kv,
"builder.properties")
if err != nil {
return err
}
@@ -773,9 +773,9 @@ func (o *runCmdOptions) applyBuildProperties() error {
return nil
}
-func convertToTraitParameter(value, traitParameter string) ([]string, error) {
+func (o *runCmdOptions) convertToTraitParameter(c client.Client, value,
traitParameter string) ([]string, error) {
traits := make([]string, 0)
- props, err := extractProperties(value)
+ props, err := o.extractProperties(c, value)
if err != nil {
return nil, err
}
@@ -852,6 +852,43 @@ func (o *runCmdOptions) GetIntegrationName(sources
[]string) string {
return name
}
+func (o *runCmdOptions) mergePropertiesWithPrecedence(c client.Client, items
[]string) (*properties.Properties, error) {
+ loPrecedenceProps := properties.NewProperties()
+ hiPrecedenceProps := properties.NewProperties()
+ for _, item := range items {
+ prop, err := o.extractProperties(c, item)
+ if err != nil {
+ return nil, err
+ }
+ // 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)
+ } else {
+ hiPrecedenceProps.Merge(prop)
+ }
+ }
+ // Any property contained in both collections will be merged
+ // giving precedence to the ones in hiPrecedenceProps
+ loPrecedenceProps.Merge(hiPrecedenceProps)
+ return loPrecedenceProps, 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 (o *runCmdOptions) extractProperties(c client.Client, value string)
(*properties.Properties, error) {
+ switch {
+ case strings.HasPrefix(value, "file:"):
+ // we already validated the existence of files during validate()
+ return loadPropertyFile(strings.Replace(value, "file:", "", 1))
+ case strings.HasPrefix(value, "secret:"):
+ return loadPropertiesFromSecret(o.Context, c, o.Namespace,
strings.Replace(value, "secret:", "", 1))
+ case strings.HasPrefix(value, "configmap:"):
+ return loadPropertiesFromConfigMap(o.Context, c, o.Namespace,
strings.Replace(value, "configmap:", "", 1))
+ default:
+ return keyValueProps(value)
+ }
+}
+
func loadPropertyFile(fileName string) (*properties.Properties, error) {
file, err := util.ReadFile(fileName)
if err != nil {
diff --git a/pkg/cmd/run_help.go b/pkg/cmd/run_help.go
index c885e4151..328e9be32 100644
--- a/pkg/cmd/run_help.go
+++ b/pkg/cmd/run_help.go
@@ -21,6 +21,7 @@ import (
"context"
"fmt"
"path"
+ "reflect"
"strings"
v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
@@ -115,37 +116,48 @@ func filterFileLocation(maybeFileLocations []string)
[]string {
return filteredOptions
}
-func mergePropertiesWithPrecedence(items []string) (*properties.Properties,
error) {
- loPrecedenceProps := properties.NewProperties()
- hiPrecedenceProps := properties.NewProperties()
- for _, item := range items {
- prop, err := extractProperties(item)
- if err != nil {
- return nil, err
- }
- // We consider file props to have a lower priority versus
single properties
- if strings.HasPrefix(item, "file:") {
- loPrecedenceProps.Merge(prop)
- } else {
- hiPrecedenceProps.Merge(prop)
- }
+func keyValueProps(value string) (*properties.Properties, error) {
+ return properties.Load([]byte(value), properties.UTF8)
+}
+
+func loadPropertiesFromSecret(ctx context.Context, c client.Client, ns string,
name string) (*properties.Properties, error) {
+ secret := kubernetes.LookupSecret(ctx, c, ns, name)
+ if secret == nil {
+ return nil, fmt.Errorf("%s secret not found in %s namespace,
make sure to provide it before the Integration can run", name, ns)
}
- // Any property contained in both collections will be merged
- // giving precedence to the ones in hiPrecedenceProps
- loPrecedenceProps.Merge(hiPrecedenceProps)
- return loPrecedenceProps, nil
+ return fromMapToProperties(secret.Data,
+ func(v reflect.Value) string { return string(v.Bytes()) },
+ func(v reflect.Value) (*properties.Properties, error) {
+ return properties.Load(v.Bytes(), properties.UTF8)
+ })
}
-// 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)
+func loadPropertiesFromConfigMap(ctx context.Context, c client.Client, ns
string, name string) (*properties.Properties, error) {
+ cm := kubernetes.LookupConfigmap(ctx, c, ns, name)
+ if cm == nil {
+ return nil, fmt.Errorf("%s configmap not found in %s namespace,
make sure to provide it before the Integration can run", name, ns)
}
- // we already validated the existence of files during validate()
- return loadPropertyFile(strings.Replace(value, "file:", "", 1))
+ return fromMapToProperties(cm.Data,
+ func(v reflect.Value) string { return v.String() },
+ func(v reflect.Value) (*properties.Properties, error) { return
keyValueProps(v.String()) })
}
-func keyValueProps(value string) (*properties.Properties, error) {
- return properties.Load([]byte(value), properties.UTF8)
+func fromMapToProperties(data interface{}, toString func(reflect.Value)
string, loadProperties func(reflect.Value) (*properties.Properties, error))
(*properties.Properties, error) {
+ result := properties.NewProperties()
+ m := reflect.ValueOf(data)
+ for _, k := range m.MapKeys() {
+ key := k.String()
+ value := m.MapIndex(k)
+ if strings.HasSuffix(key, ".properties") {
+ p, err := loadProperties(value)
+ if err == nil {
+ result.Merge(p)
+ } else if _, _, err = result.Set(key, toString(value));
err != nil {
+ return nil, fmt.Errorf("cannot assign %s to
%s", value, key)
+ }
+ } else if _, _, err := result.Set(key, toString(value)); err !=
nil {
+ return nil, fmt.Errorf("cannot assign %s to %s", value,
key)
+ }
+ }
+ return result, nil
}
diff --git a/pkg/cmd/run_help_test.go b/pkg/cmd/run_help_test.go
index 0fa7c9a96..175d4da93 100644
--- a/pkg/cmd/run_help_test.go
+++ b/pkg/cmd/run_help_test.go
@@ -18,8 +18,6 @@ limitations under the License.
package cmd
import (
- "io/ioutil"
- "os"
"testing"
"github.com/stretchr/testify/assert"
@@ -41,64 +39,3 @@ func TestFilterFileLocation(t *testing.T) {
assert.Equal(t, "app.properties", filteredOptions[1])
assert.Equal(t, "/validfile", filteredOptions[2])
}
-
-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
- `), 0o400))
-
- 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 TestExtractPropertiesFromFileAndSingleValue(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
- `), 0o400))
-
- properties := []string{"key=override", "file:" + tmpFile1.Name(),
"my.key = override"}
- props, err := mergePropertiesWithPrecedence(properties)
- assert.Nil(t, err)
- assert.Equal(t, 2, props.Len())
- val, ok := props.Get("key")
- assert.True(t, ok)
- assert.Equal(t, "override", val)
- val, ok = props.Get("my.key")
- assert.True(t, ok)
- assert.Equal(t, "override", val)
-}
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index b02f37698..2b7144071 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -274,6 +274,69 @@ f=g:h
i=j\nk
`
+func TestExtractProperties_SingleKeyValue(t *testing.T) {
+ correctValues := []string{"key=val", "key = val", "key= val", " key =
val"}
+ runCmdOptions, _, _ := initializeRunCmdOptionsWithOutput(t)
+ for _, val := range correctValues {
+ prop, err := runCmdOptions.extractProperties(nil, 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
+ `), 0o400))
+ runCmdOptions, _, _ := initializeRunCmdOptionsWithOutput(t)
+ props, err := runCmdOptions.extractProperties(nil,
"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 TestExtractPropertiesFromFileAndSingleValue(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
+ `), 0o400))
+
+ properties := []string{"key=override", "file:" + tmpFile1.Name(),
"my.key = override"}
+ runCmdOptions, _, _ := initializeRunCmdOptionsWithOutput(t)
+ props, err := runCmdOptions.mergePropertiesWithPrecedence(nil,
properties)
+ assert.Nil(t, err)
+ assert.Equal(t, 2, props.Len())
+ val, ok := props.Get("key")
+ assert.True(t, ok)
+ assert.Equal(t, "override", val)
+ val, ok = props.Get("my.key")
+ assert.True(t, ok)
+ assert.Equal(t, "override", val)
+}
+
func TestAddPropertyFile(t *testing.T) {
var tmpFile *os.File
var err error
@@ -284,7 +347,8 @@ func TestAddPropertyFile(t *testing.T) {
assert.Nil(t, tmpFile.Close())
assert.Nil(t, ioutil.WriteFile(tmpFile.Name(),
[]byte(TestPropertyFileContent), 0o400))
- properties, err := convertToTraitParameter("file:"+tmpFile.Name(),
"trait.properties")
+ runCmdOptions, _, _ := initializeRunCmdOptionsWithOutput(t)
+ properties, err := runCmdOptions.convertToTraitParameter(nil,
"file:"+tmpFile.Name(), "trait.properties")
assert.Nil(t, err)
assert.Equal(t, 3, len(properties))
assert.Equal(t, `trait.properties=a = b`, properties[0])
@@ -293,7 +357,8 @@ func TestAddPropertyFile(t *testing.T) {
}
func TestRunProperty(t *testing.T) {
- properties, err := convertToTraitParameter(`key=value\nnewline`,
"trait.properties")
+ runCmdOptions, _, _ := initializeRunCmdOptionsWithOutput(t)
+ properties, err := runCmdOptions.convertToTraitParameter(nil,
`key=value\nnewline`, "trait.properties")
assert.Nil(t, err)
assert.Equal(t, 1, len(properties))
assert.Equal(t, `trait.properties=key = value\nnewline`, properties[0])