Skip to content

Commit

Permalink
feat(trait): support Camel cloud native properties
Browse files Browse the repository at this point in the history
Beside that, we're deprecating the access to Secret and Configmap by either the CLI or the operator

Closes #5863
  • Loading branch information
squakez authored and gansheer committed Oct 19, 2024
1 parent 699810e commit 9ea0ef9
Show file tree
Hide file tree
Showing 13 changed files with 439 additions and 530 deletions.
66 changes: 1 addition & 65 deletions docs/modules/ROOT/pages/configuration/build-time-properties.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -60,74 +60,10 @@ kamel run --build-property=file:quarkus.properties build-property-route.yaml

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,yaml]
.build-property-route.yaml
----
- from:
uri: "timer:build-property"
steps:
- setBody:
simple: "The application name: {{quarkus.application.name}}"
- to: "log:info"
----

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.yaml
----

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.yaml
----

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/configmap/secret, 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, then, the single option will have higher priority and will be used.

[[build-time-runtime-conf]]
== Run time properties
Expand Down
65 changes: 2 additions & 63 deletions docs/modules/ROOT/pages/configuration/camel-properties.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -83,73 +83,12 @@ The property file is parsed and its properties configured on the `Integration`.
[[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,yaml]
.config-property-configmap-route.yaml
----
- from:
uri: "timer:property"
parameters:
period: "{{period}}"
steps:
- setBody:
simple: "Hello {{name}}!"
- to: "log:info"
----

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 config-property-configmap-route.yaml
----

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 config-property-configmap-route.yaml
----

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.
In case some runtime properties are stored into a `Configmap` or a `Secret`, you can use the `--config` parameter 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. See more advanced details on the `mount` trait.

[[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/configmap/secret, 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, then, the single option will have higher priority and will be used.

[[runtime-build-time-conf]]
== Build time properties
Expand Down
102 changes: 65 additions & 37 deletions pkg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,22 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions)

cmd.Flags().String("name", "", "The integration name")
cmd.Flags().String("image", "", "An image built externally (ie, via CICD). Enabling it will skip the Integration build phase.")
cmd.Flags().StringArrayP("connect", "c", nil, "A Service that the integration should bind to, specified as [[apigroup/]version:]kind:[namespace/]name")
// Deprecated: service binding parameter won't be supported in future releases.
cmd.Flags().StringArrayP("connect", "c", nil, "A Service that the integration should bind to, specified as [[apigroup/]version:]kind:[namespace/]name."+
"Deprecated: service binding won't be supported in future releases.")
cmd.Flags().StringArrayP("dependency", "d", nil, `A dependency that should be included, e.g., "-d camel:mail" for a Camel component, "-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 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 or a Secret (syntax: [configmap|secret]:name[/key], where name represents 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 or a Secret (syntax: [configmap|secret]:name[/key][@path], where name represents the configmap/secret name, key optionally represents the configmap/secret key to be filtered and path represents the destination path)")
cmd.Flags().StringArrayP("property", "p", nil, "Add a runtime property or a local properties file from a path "+
"(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 from a path "+
"(syntax: [my-key=my-value|file:/path/to/my-conf.properties])")
cmd.Flags().StringArray("config", nil, "Add a runtime configuration from a Configmap or a Secret "+
"(syntax: [configmap|secret]:name[/key], where name represents 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 or a Secret "+
"(syntax: [configmap|secret]:name[/key][@path], where name represents 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")
cmd.Flags().Bool("logs", false, "Print integration logs")
cmd.Flags().Bool("sync", false, "Synchronize the local source file with the cluster, republishing at each change")
Expand Down Expand Up @@ -121,38 +129,41 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions)

type runCmdOptions struct {
*RootCmdOptions `json:"-"`
Compression bool `mapstructure:"compression" yaml:",omitempty"`
Wait bool `mapstructure:"wait" yaml:",omitempty"`
Logs bool `mapstructure:"logs" yaml:",omitempty"`
Sync bool `mapstructure:"sync" yaml:",omitempty"`
Dev bool `mapstructure:"dev" yaml:",omitempty"`
UseFlows bool `mapstructure:"use-flows" yaml:",omitempty"`
Save bool `mapstructure:"save" yaml:",omitempty" kamel:"omitsave"`
IntegrationKit string `mapstructure:"kit" yaml:",omitempty"`
IntegrationName string `mapstructure:"name" yaml:",omitempty"`
ContainerImage string `mapstructure:"image" yaml:",omitempty"`
Profile string `mapstructure:"profile" yaml:",omitempty"`
IntegrationProfile string `mapstructure:"integration-profile" yaml:",omitempty"`
OperatorID string `mapstructure:"operator-id" yaml:",omitempty"`
OutputFormat string `mapstructure:"output" yaml:",omitempty"`
PodTemplate string `mapstructure:"pod-template" yaml:",omitempty"`
ServiceAccount string `mapstructure:"service-account" yaml:",omitempty"`
Connects []string `mapstructure:"connects" yaml:",omitempty"`
Resources []string `mapstructure:"resources" yaml:",omitempty"`
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"`
Configs []string `mapstructure:"configs" yaml:",omitempty"`
Repositories []string `mapstructure:"maven-repositories" yaml:",omitempty"`
Traits []string `mapstructure:"traits" yaml:",omitempty"`
Volumes []string `mapstructure:"volumes" yaml:",omitempty"`
EnvVars []string `mapstructure:"envs" yaml:",omitempty"`
Labels []string `mapstructure:"labels" yaml:",omitempty"`
Annotations []string `mapstructure:"annotations" yaml:",omitempty"`
Sources []string `mapstructure:"sources" yaml:",omitempty"`
RegistryOptions url.Values
Force bool `mapstructure:"force" yaml:",omitempty"`
Compression bool `mapstructure:"compression" yaml:",omitempty"`
Wait bool `mapstructure:"wait" yaml:",omitempty"`
Logs bool `mapstructure:"logs" yaml:",omitempty"`
Sync bool `mapstructure:"sync" yaml:",omitempty"`
Dev bool `mapstructure:"dev" yaml:",omitempty"`
UseFlows bool `mapstructure:"use-flows" yaml:",omitempty"`
Save bool `mapstructure:"save" yaml:",omitempty" kamel:"omitsave"`
IntegrationKit string `mapstructure:"kit" yaml:",omitempty"`
IntegrationName string `mapstructure:"name" yaml:",omitempty"`
ContainerImage string `mapstructure:"image" yaml:",omitempty"`
Profile string `mapstructure:"profile" yaml:",omitempty"`
IntegrationProfile string `mapstructure:"integration-profile" yaml:",omitempty"`
OperatorID string `mapstructure:"operator-id" yaml:",omitempty"`
OutputFormat string `mapstructure:"output" yaml:",omitempty"`
PodTemplate string `mapstructure:"pod-template" yaml:",omitempty"`
ServiceAccount string `mapstructure:"service-account" yaml:",omitempty"`
// Deprecated: service binding parameter won't be supported in future releases.
Connects []string `mapstructure:"connects" yaml:",omitempty"`
Resources []string `mapstructure:"resources" yaml:",omitempty"`
// Deprecated: openapi parameter won't be supported in future releases.
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"`
Configs []string `mapstructure:"configs" yaml:",omitempty"`
Repositories []string `mapstructure:"maven-repositories" yaml:",omitempty"`
Traits []string `mapstructure:"traits" yaml:",omitempty"`
Volumes []string `mapstructure:"volumes" yaml:",omitempty"`
EnvVars []string `mapstructure:"envs" yaml:",omitempty"`
Labels []string `mapstructure:"labels" yaml:",omitempty"`
Annotations []string `mapstructure:"annotations" yaml:",omitempty"`
Sources []string `mapstructure:"sources" yaml:",omitempty"`
// Deprecated: registry parameter no longer in use.
RegistryOptions url.Values
Force bool `mapstructure:"force" yaml:",omitempty"`
}

func (o *runCmdOptions) decode(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -275,6 +286,23 @@ func (o *runCmdOptions) validate(cmd *cobra.Command) error {
}
}

for i, property := range o.Properties {
// We support only --config
if strings.HasPrefix(property, "configmap:") || strings.HasPrefix(property, "secret:") {
o.Configs = append(o.Configs, property)
// clean it to avoid further processing
o.Properties[i] = ""
fmt.Fprintf(cmd.OutOrStdout(), "Property %s is deprecated: use --config %s instead\n", property, property)
}
}

for _, bp := range o.BuildProperties {
// Deprecated: to be removed
if strings.HasPrefix(bp, "configmap:") || strings.HasPrefix(bp, "secret:") {
fmt.Fprintf(cmd.OutOrStdout(), "Build property %s is deprecated. It will be removed from future releases.\n", bp)
}
}

var client client.Client
if !isOfflineCommand(cmd) {
client, err = o.GetCmdClient()
Expand Down
9 changes: 9 additions & 0 deletions pkg/cmd/run_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ func keyValueProps(value string) (*properties.Properties, error) {
return properties.Load([]byte(value), properties.UTF8)
}

// Deprecated: won't be supported in future releases.
func loadPropertiesFromSecret(ctx context.Context, c client.Client, ns string, name string) (*properties.Properties, error) {
if c == nil {
return nil, fmt.Errorf("cannot inspect Secrets in offline mode")
}
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)
Expand All @@ -88,7 +92,11 @@ func loadPropertiesFromSecret(ctx context.Context, c client.Client, ns string, n
})
}

// Deprecated: won't be supported in future releases.
func loadPropertiesFromConfigMap(ctx context.Context, c client.Client, ns string, name string) (*properties.Properties, error) {
if c == nil {
return nil, fmt.Errorf("cannot inspect Configmaps in offline mode")
}
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)
Expand All @@ -98,6 +106,7 @@ func loadPropertiesFromConfigMap(ctx context.Context, c client.Client, ns string
func(v reflect.Value) (*properties.Properties, error) { return keyValueProps(v.String()) })
}

// Deprecated: func supporting other deprecated funcs.
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)
Expand Down
5 changes: 1 addition & 4 deletions pkg/cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ func TestTrait(t *testing.T) {
fileName := filepath.Base(tmpFile.Name())

runCmdOptions, runCmd, _ := initializeRunCmdOptionsWithOutput(t)
output, err := test.ExecuteCommand(runCmd, cmdRun, tmpFile.Name(), "-o", "yaml", "-t", "mount.configs=configmap:my-cm", "--connect", "my-service-binding")
output, err := test.ExecuteCommand(runCmd, cmdRun, tmpFile.Name(), "-o", "yaml", "-t", "mount.configs=configmap:my-cm")
assert.Equal(t, "yaml", runCmdOptions.OutputFormat)

require.NoError(t, err)
Expand All @@ -646,9 +646,6 @@ spec:
mount:
configs:
- configmap:my-cm
service-binding:
services:
- my-service-binding
status: {}
`, fileName, fileName), output)
}
Expand Down
Loading

0 comments on commit 9ea0ef9

Please sign in to comment.