From b39062ae6de183e0d8ee5251a05010243bc2a8ea Mon Sep 17 00:00:00 2001 From: Pasquale Congiusti Date: Sat, 19 Oct 2024 12:20:02 +0200 Subject: [PATCH] feat(trait): support Camel cloud native properties Beside that, we're deprecating the access to Secret and Configmap by either the CLI or the operator Closes #5863 --- .../configuration/build-time-properties.adoc | 66 +------ .../pages/configuration/camel-properties.adoc | 65 +------ pkg/cmd/run.go | 102 ++++++---- pkg/cmd/run_support.go | 9 + pkg/cmd/run_test.go | 5 +- pkg/trait/camel.go | 38 ++-- pkg/trait/container.go | 6 - pkg/trait/mount.go | 173 ++++++++++++++++- pkg/trait/mount_test.go | 164 +++++++++++++++++ pkg/trait/trait_test.go | 174 ------------------ pkg/trait/trait_types.go | 152 +-------------- pkg/trait/trait_types_test.go | 13 -- pkg/util/kubernetes/lookup.go | 2 + 13 files changed, 439 insertions(+), 530 deletions(-) diff --git a/docs/modules/ROOT/pages/configuration/build-time-properties.adoc b/docs/modules/ROOT/pages/configuration/build-time-properties.adoc index 88c3991c68..7c9f99c261 100644 --- a/docs/modules/ROOT/pages/configuration/build-time-properties.adoc +++ b/docs/modules/ROOT/pages/configuration/build-time-properties.adoc @@ -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 diff --git a/docs/modules/ROOT/pages/configuration/camel-properties.adoc b/docs/modules/ROOT/pages/configuration/camel-properties.adoc index d474220f86..254b360c7e 100644 --- a/docs/modules/ROOT/pages/configuration/camel-properties.adoc +++ b/docs/modules/ROOT/pages/configuration/camel-properties.adoc @@ -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 diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go index b4667868d9..b416b9803f 100644 --- a/pkg/cmd/run.go +++ b/pkg/cmd/run.go @@ -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") @@ -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 { @@ -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() diff --git a/pkg/cmd/run_support.go b/pkg/cmd/run_support.go index af2d7a3fe8..358e84fd00 100644 --- a/pkg/cmd/run_support.go +++ b/pkg/cmd/run_support.go @@ -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) @@ -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) @@ -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) diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go index 03a63c4565..dfee3e71cc 100644 --- a/pkg/cmd/run_test.go +++ b/pkg/cmd/run_test.go @@ -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) @@ -646,9 +646,6 @@ spec: mount: configs: - configmap:my-cm - service-binding: - services: - - my-service-binding status: {} `, fileName, fileName), output) } diff --git a/pkg/trait/camel.go b/pkg/trait/camel.go index df94312aec..53eccf51c3 100644 --- a/pkg/trait/camel.go +++ b/pkg/trait/camel.go @@ -135,9 +135,7 @@ func (t *camelTrait) Apply(e *Environment) error { } if e.IntegrationInRunningPhases() { - // Get all resources - maps := t.computeConfigMaps(e) - e.Resources.AddAll(maps) + e.Resources.AddAll(t.computeUserProperties(e)) } return nil } @@ -181,7 +179,23 @@ func (t *camelTrait) loadOrCreateCatalog(e *Environment, runtimeVersion string) return nil } -func (t *camelTrait) computeConfigMaps(e *Environment) []ctrl.Object { +func determineRuntimeVersion(e *Environment) (string, error) { + if e.Integration != nil && e.Integration.Status.RuntimeVersion != "" { + return e.Integration.Status.RuntimeVersion, nil + } + if e.IntegrationKit != nil && e.IntegrationKit.Status.RuntimeVersion != "" { + return e.IntegrationKit.Status.RuntimeVersion, nil + } + if e.IntegrationProfile != nil && e.IntegrationProfile.Status.Build.RuntimeVersion != "" { + return e.IntegrationProfile.Status.Build.RuntimeVersion, nil + } + if e.Platform != nil && e.Platform.Status.Build.RuntimeVersion != "" { + return e.Platform.Status.Build.RuntimeVersion, nil + } + return "", errors.New("unable to determine runtime version") +} + +func (t *camelTrait) computeUserProperties(e *Environment) []ctrl.Object { sources := e.Integration.AllSources() maps := make([]ctrl.Object, 0, len(sources)+1) @@ -261,19 +275,3 @@ func (t *camelTrait) computeConfigMaps(e *Environment) []ctrl.Object { return maps } - -func determineRuntimeVersion(e *Environment) (string, error) { - if e.Integration != nil && e.Integration.Status.RuntimeVersion != "" { - return e.Integration.Status.RuntimeVersion, nil - } - if e.IntegrationKit != nil && e.IntegrationKit.Status.RuntimeVersion != "" { - return e.IntegrationKit.Status.RuntimeVersion, nil - } - if e.IntegrationProfile != nil && e.IntegrationProfile.Status.Build.RuntimeVersion != "" { - return e.IntegrationProfile.Status.Build.RuntimeVersion, nil - } - if e.Platform != nil && e.Platform.Status.Build.RuntimeVersion != "" { - return e.Platform.Status.Build.RuntimeVersion, nil - } - return "", errors.New("unable to determine runtime version") -} diff --git a/pkg/trait/container.go b/pkg/trait/container.go index f3f24deb3b..927ea05602 100644 --- a/pkg/trait/container.go +++ b/pkg/trait/container.go @@ -188,12 +188,6 @@ func (t *containerTrait) configureContainer(e *Environment) error { }); err != nil { return err } - e.addSourcesProperties() - if props, err := e.computeApplicationProperties(); err != nil { - return err - } else if props != nil { - e.Resources.Add(props) - } t.configureResources(&container) if knative || ptr.Deref(t.Expose, false) { t.configureService(e, &container, knative) diff --git a/pkg/trait/mount.go b/pkg/trait/mount.go index 33a9b85fc4..b15c9765d8 100644 --- a/pkg/trait/mount.go +++ b/pkg/trait/mount.go @@ -19,6 +19,7 @@ package trait import ( "fmt" + "path" "path/filepath" "strings" @@ -27,12 +28,17 @@ import ( corev1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" serving "knative.dev/serving/pkg/apis/serving/v1" + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" "github.com/apache/camel-k/v2/pkg/util/boolean" + "github.com/apache/camel-k/v2/pkg/util/camel" "github.com/apache/camel-k/v2/pkg/util/kubernetes" + "github.com/apache/camel-k/v2/pkg/util/log" + "github.com/apache/camel-k/v2/pkg/util/property" utilResource "github.com/apache/camel-k/v2/pkg/util/resource" ) @@ -112,22 +118,34 @@ func (t *mountTrait) Apply(e *Environment) error { } if visited { - // Volumes declared in the Integration resources - e.configureVolumesAndMounts(volumes, &container.VolumeMounts) // Volumes declared in the trait config/resource options + // as this func influences the application.properties + // must be set as the first one to execute err := t.configureVolumesAndMounts(e, volumes, &container.VolumeMounts) if err != nil { return err } + // Here we configure the application.properties + t.addSourcesProperties(e) + if props, err := t.computeApplicationProperties(e); err != nil { + return err + } else if props != nil { + e.Resources.Add(props) + } + // Volumes declared in the Integration resources (including the application.properties Configmap) + t.configureCamelVolumesAndMounts(e, volumes, &container.VolumeMounts) } return nil } +// configureVolumesAndMounts is in charge to mount volumes and mounts coming from the trait configuration. func (t *mountTrait) configureVolumesAndMounts(e *Environment, vols *[]corev1.Volume, mnts *[]corev1.VolumeMount) error { for _, c := range t.Configs { if conf, parseErr := utilResource.ParseConfig(c); parseErr == nil { - t.mountResource(vols, mnts, conf) + // Let Camel parse these resources as properties + destFilePath := t.mountResource(vols, mnts, conf) + e.appendCloudPropertiesLocation(destFilePath) } else { return parseErr } @@ -158,7 +176,78 @@ func (t *mountTrait) configureVolumesAndMounts(e *Environment, vols *[]corev1.Vo return nil } -func (t *mountTrait) mountResource(vols *[]corev1.Volume, mnts *[]corev1.VolumeMount, conf *utilResource.Config) { +// configureCamelVolumesAndMounts is in charge to mount volumes and mounts coming from Camel configuration +// (ie, sources, properties, kamelets, etcetera). +func (t *mountTrait) configureCamelVolumesAndMounts(e *Environment, vols *[]corev1.Volume, mnts *[]corev1.VolumeMount) { + // Sources + idx := 0 + for _, s := range e.Integration.AllSources() { + // We don't process routes embedded (native) or Kamelets + if e.isEmbedded(s) || s.IsGeneratedFromKamelet() { + continue + } + // Routes are copied under /etc/camel/sources and discovered by the runtime accordingly + cmName := fmt.Sprintf("%s-source-%03d", e.Integration.Name, idx) + if s.ContentRef != "" { + cmName = s.ContentRef + } + cmKey := "content" + if s.ContentKey != "" { + cmKey = s.ContentKey + } + resName := strings.TrimPrefix(s.Name, "/") + refName := fmt.Sprintf("i-source-%03d", idx) + resPath := filepath.Join(camel.SourcesMountPath, resName) + vol := getVolume(refName, "configmap", cmName, cmKey, resName) + mnt := getMount(refName, resPath, resName, true) + + *vols = append(*vols, *vol) + *mnts = append(*mnts, *mnt) + idx++ + } + // Resources (likely application properties or kamelets) + if e.Resources != nil { + e.Resources.VisitConfigMap(func(configMap *corev1.ConfigMap) { + switch configMap.Labels[kubernetes.ConfigMapTypeLabel] { + case CamelPropertiesType: + // Camel properties + propertiesType := configMap.Labels["camel.apache.org/properties.type"] + resName := propertiesType + ".properties" + + var mountPath string + switch propertiesType { + case "application": + mountPath = filepath.Join(camel.BasePath, resName) + case "user": + mountPath = filepath.Join(camel.ConfDPath, resName) + } + + if propertiesType != "" { + refName := propertiesType + "-properties" + vol := getVolume(refName, "configmap", configMap.Name, "application.properties", resName) + mnt := getMount(refName, mountPath, resName, true) + + *vols = append(*vols, *vol) + *mnts = append(*mnts, *mnt) + } else { + log.WithValues("Function", "trait.configureVolumesAndMounts").Infof("Warning: could not determine camel properties type %s", propertiesType) + } + case KameletBundleType: + // Kamelets bundle configmap + kameletMountPoint := configMap.Annotations[kameletMountPointAnnotation] + refName := KameletBundleType + vol := getVolume(refName, "configmap", configMap.Name, "", "") + mnt := getMount(refName, kameletMountPoint, "", true) + + *vols = append(*vols, *vol) + *mnts = append(*mnts, *mnt) + } + }) + } +} + +// mountResource add the resource to volumes and mounts and return the final path where the resource is mounted. +func (t *mountTrait) mountResource(vols *[]corev1.Volume, mnts *[]corev1.VolumeMount, conf *utilResource.Config) string { refName := kubernetes.SanitizeLabel(conf.Name()) dstDir := conf.DestinationPath() dstFile := "" @@ -180,6 +269,8 @@ func (t *mountTrait) mountResource(vols *[]corev1.Volume, mnts *[]corev1.VolumeM *vols = append(*vols, *vol) *mnts = append(*mnts, *mnt) + + return mnt.MountPath } func (t *mountTrait) addServiceBindingSecret(e *Environment) { @@ -271,3 +362,77 @@ func createPVC(e *Environment, volumeParts []string) error { return nil } + +// computeApplicationProperties is in charge to configure the configmap containing Camel application.properties. +func (t *mountTrait) computeApplicationProperties(e *Environment) (*corev1.ConfigMap, error) { + // application properties + applicationProperties, err := property.EncodePropertyFile(e.ApplicationProperties) + if err != nil { + return nil, fmt.Errorf("could not compute application properties: %w", err) + } + + if applicationProperties != "" { + return &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: e.Integration.Name + "-application-properties", + Namespace: e.Integration.Namespace, + Labels: map[string]string{ + v1.IntegrationLabel: e.Integration.Name, + "camel.apache.org/properties.type": "application", + kubernetes.ConfigMapTypeLabel: CamelPropertiesType, + }, + }, + Data: map[string]string{ + "application.properties": applicationProperties, + }, + }, nil + } + + return nil, nil +} + +// addSourcesProperties is in charge to add the sources in the application.properties required by Camel K Runtime. +func (t *mountTrait) addSourcesProperties(e *Environment) { + if e.ApplicationProperties == nil { + e.ApplicationProperties = make(map[string]string) + } + idx := 0 + for _, s := range e.Integration.AllSources() { + // We don't process routes embedded (native) or Kamelets + if e.isEmbedded(s) || s.IsGeneratedFromKamelet() { + continue + } + srcName := strings.TrimPrefix(filepath.ToSlash(s.Name), "/") + src := "file:" + path.Join(filepath.ToSlash(camel.SourcesMountPath), srcName) + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].location", idx)] = src + + simpleName := srcName + if strings.Contains(srcName, ".") { + simpleName = srcName[0:strings.Index(srcName, ".")] + } + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].name", idx)] = simpleName + + for pid, p := range s.PropertyNames { + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].property-names[%d]", idx, pid)] = p + } + + if s.Type != "" { + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].type", idx)] = string(s.Type) + } + if s.InferLanguage() != "" { + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].language", idx)] = string(s.InferLanguage()) + } + if s.Loader != "" { + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].loader", idx)] = s.Loader + } + if s.Compression { + e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].compressed", idx)] = boolean.TrueString + } + + idx++ + } +} diff --git a/pkg/trait/mount_test.go b/pkg/trait/mount_test.go index df551dbf5b..7013801935 100644 --- a/pkg/trait/mount_test.go +++ b/pkg/trait/mount_test.go @@ -31,6 +31,7 @@ import ( v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" + "github.com/apache/camel-k/v2/pkg/util/boolean" "github.com/apache/camel-k/v2/pkg/util/camel" "github.com/apache/camel-k/v2/pkg/util/gzip" "github.com/apache/camel-k/v2/pkg/util/kubernetes" @@ -341,3 +342,166 @@ func TestMountVolumesCreateUserStorageClass(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, pvc) } + +func TestConfigureVolumesAndMountsSources(t *testing.T) { + trait, _ := newMountTrait().(*mountTrait) + env := Environment{ + Resources: kubernetes.NewCollection(), + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: TestDeploymentName, + Namespace: "ns", + }, + Spec: v1.IntegrationSpec{ + Sources: []v1.SourceSpec{ + { + DataSpec: v1.DataSpec{ + Name: "source1.java", + ContentRef: "my-cm1", + ContentKey: "source1.java", + }, + Type: "data", + }, + { + DataSpec: v1.DataSpec{ + Name: "source2.java", + ContentRef: "my-cm2", + }, + Type: "data", + }, + }, + }, + }, + Catalog: &Catalog{}, + } + + vols := make([]corev1.Volume, 0) + mnts := make([]corev1.VolumeMount, 0) + + trait.configureCamelVolumesAndMounts(&env, &vols, &mnts) + + assert.Len(t, vols, 2) + assert.Len(t, mnts, 2) + + v := findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm1" }) + assert.NotNil(t, v) + assert.NotNil(t, v.VolumeSource.ConfigMap) + assert.Len(t, v.VolumeSource.ConfigMap.Items, 1) + assert.Equal(t, "source1.java", v.VolumeSource.ConfigMap.Items[0].Key) + + m := findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name }) + assert.NotNil(t, m) + + v = findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm2" }) + assert.NotNil(t, v) + assert.NotNil(t, v.VolumeSource.ConfigMap) + assert.Len(t, v.VolumeSource.ConfigMap.Items, 1) + assert.Equal(t, "content", v.VolumeSource.ConfigMap.Items[0].Key) + + m = findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name }) + assert.NotNil(t, m) +} + +func TestConfigureVolumesAndMountsSourcesInNativeMode(t *testing.T) { + trait, _ := newMountTrait().(*mountTrait) + traitList := make([]Trait, 0, len(FactoryList)) + quarkus, ok := newQuarkusTrait().(*quarkusTrait) + assert.True(t, ok, "A Quarkus trait was expected") + quarkus.Modes = []traitv1.QuarkusMode{traitv1.NativeQuarkusMode} + traitList = append(traitList, quarkus) + env := Environment{ + Resources: kubernetes.NewCollection(), + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: TestDeploymentName, + Namespace: "ns", + }, + Spec: v1.IntegrationSpec{ + Sources: []v1.SourceSpec{ + { + DataSpec: v1.DataSpec{ + Name: "source1.xml", + ContentRef: "my-cm1", + ContentKey: "source1.xml", + }, + Type: "data", + }, + { + DataSpec: v1.DataSpec{ + Name: "source2.java", + ContentRef: "my-cm2", + }, + Type: "data", + }, + { + DataSpec: v1.DataSpec{ + Name: "source1.java", + ContentRef: "my-cm3", + ContentKey: "source1.java", + }, + Type: "data", + }, + { + DataSpec: v1.DataSpec{ + Name: "source2.xml", + ContentRef: "my-cm4", + }, + Type: "data", + }, + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + }, + }, + IntegrationKit: &v1.IntegrationKit{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.IntegrationKitLayoutLabel: v1.IntegrationKitLayoutNativeSources, + }, + Namespace: "ns", + }, + }, + Catalog: &Catalog{ + traits: traitList, + }, + CamelCatalog: &camel.RuntimeCatalog{ + CamelCatalogSpec: v1.CamelCatalogSpec{ + Loaders: map[string]v1.CamelLoader{ + "java": { + Metadata: map[string]string{ + "native": boolean.TrueString, + "sources-required-at-build-time": boolean.TrueString, + }, + }, + }, + }, + }, + } + + vols := make([]corev1.Volume, 0) + mnts := make([]corev1.VolumeMount, 0) + + trait.configureCamelVolumesAndMounts(&env, &vols, &mnts) + + assert.Len(t, vols, 2) + assert.Len(t, mnts, 2) + + v := findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm1" }) + assert.NotNil(t, v) + assert.NotNil(t, v.VolumeSource.ConfigMap) + assert.Len(t, v.VolumeSource.ConfigMap.Items, 1) + assert.Equal(t, "source1.xml", v.VolumeSource.ConfigMap.Items[0].Key) + + m := findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name }) + assert.NotNil(t, m) + + v = findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm4" }) + assert.NotNil(t, v) + assert.NotNil(t, v.VolumeSource.ConfigMap) + assert.Len(t, v.VolumeSource.ConfigMap.Items, 1) + assert.Equal(t, "content", v.VolumeSource.ConfigMap.Items[0].Key) + + m = findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name }) + assert.NotNil(t, m) +} diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go index 105e4eace7..2324dd8cf1 100644 --- a/pkg/trait/trait_test.go +++ b/pkg/trait/trait_test.go @@ -36,7 +36,6 @@ import ( v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" "github.com/apache/camel-k/v2/pkg/resources" - "github.com/apache/camel-k/v2/pkg/util/boolean" "github.com/apache/camel-k/v2/pkg/util/camel" "github.com/apache/camel-k/v2/pkg/util/defaults" "github.com/apache/camel-k/v2/pkg/util/kubernetes" @@ -56,9 +55,6 @@ func TestOpenShiftTraits(t *testing.T) { assert.Nil(t, env.GetTrait("service")) assert.Nil(t, env.GetTrait("route")) assert.NotNil(t, env.GetTrait("owner")) - assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool { - return cm.Labels["camel.apache.org/properties.type"] != "" - })) assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool { return deployment.Name == TestDeploymentName })) @@ -71,9 +67,6 @@ func TestOpenShiftTraitsWithWeb(t *testing.T) { assert.NotNil(t, env.GetTrait("service")) assert.NotNil(t, env.GetTrait("route")) assert.NotNil(t, env.GetTrait("owner")) - assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool { - return cm.Labels["camel.apache.org/properties.type"] != "" - })) assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool { return deployment.Name == TestDeploymentName })) @@ -117,9 +110,6 @@ func TestKubernetesTraits(t *testing.T) { assert.Nil(t, env.GetTrait("service")) assert.Nil(t, env.GetTrait("route")) assert.NotNil(t, env.GetTrait("owner")) - assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool { - return cm.Labels["camel.apache.org/properties.type"] != "" - })) assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool { return deployment.Name == TestDeploymentName })) @@ -132,9 +122,6 @@ func TestKubernetesTraitsWithWeb(t *testing.T) { assert.NotNil(t, env.GetTrait("service")) assert.Nil(t, env.GetTrait("route")) assert.NotNil(t, env.GetTrait("owner")) - assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool { - return cm.Labels["camel.apache.org/properties.type"] != "" - })) assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool { return deployment.Name == TestDeploymentName })) @@ -189,167 +176,6 @@ func TestTraitHierarchyDecode(t *testing.T) { assert.Equal(t, 15, *kns.Target) } -func TestConfigureVolumesAndMountsSources(t *testing.T) { - env := Environment{ - Resources: kubernetes.NewCollection(), - Integration: &v1.Integration{ - ObjectMeta: metav1.ObjectMeta{ - Name: TestDeploymentName, - Namespace: "ns", - }, - Spec: v1.IntegrationSpec{ - Sources: []v1.SourceSpec{ - { - DataSpec: v1.DataSpec{ - Name: "source1.java", - ContentRef: "my-cm1", - ContentKey: "source1.java", - }, - Type: "data", - }, - { - DataSpec: v1.DataSpec{ - Name: "source2.java", - ContentRef: "my-cm2", - }, - Type: "data", - }, - }, - }, - }, - Catalog: &Catalog{}, - } - - vols := make([]corev1.Volume, 0) - mnts := make([]corev1.VolumeMount, 0) - - env.configureVolumesAndMounts(&vols, &mnts) - - assert.Len(t, vols, 2) - assert.Len(t, mnts, 2) - - v := findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm1" }) - assert.NotNil(t, v) - assert.NotNil(t, v.VolumeSource.ConfigMap) - assert.Len(t, v.VolumeSource.ConfigMap.Items, 1) - assert.Equal(t, "source1.java", v.VolumeSource.ConfigMap.Items[0].Key) - - m := findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name }) - assert.NotNil(t, m) - - v = findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm2" }) - assert.NotNil(t, v) - assert.NotNil(t, v.VolumeSource.ConfigMap) - assert.Len(t, v.VolumeSource.ConfigMap.Items, 1) - assert.Equal(t, "content", v.VolumeSource.ConfigMap.Items[0].Key) - - m = findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name }) - assert.NotNil(t, m) -} - -func TestConfigureVolumesAndMountsSourcesInNativeMode(t *testing.T) { - traitList := make([]Trait, 0, len(FactoryList)) - trait, ok := newQuarkusTrait().(*quarkusTrait) - assert.True(t, ok, "A Quarkus trait was expected") - trait.Modes = []traitv1.QuarkusMode{traitv1.NativeQuarkusMode} - traitList = append(traitList, trait) - env := Environment{ - Resources: kubernetes.NewCollection(), - Integration: &v1.Integration{ - ObjectMeta: metav1.ObjectMeta{ - Name: TestDeploymentName, - Namespace: "ns", - }, - Spec: v1.IntegrationSpec{ - Sources: []v1.SourceSpec{ - { - DataSpec: v1.DataSpec{ - Name: "source1.xml", - ContentRef: "my-cm1", - ContentKey: "source1.xml", - }, - Type: "data", - }, - { - DataSpec: v1.DataSpec{ - Name: "source2.java", - ContentRef: "my-cm2", - }, - Type: "data", - }, - { - DataSpec: v1.DataSpec{ - Name: "source1.java", - ContentRef: "my-cm3", - ContentKey: "source1.java", - }, - Type: "data", - }, - { - DataSpec: v1.DataSpec{ - Name: "source2.xml", - ContentRef: "my-cm4", - }, - Type: "data", - }, - }, - }, - Status: v1.IntegrationStatus{ - Phase: v1.IntegrationPhaseRunning, - }, - }, - IntegrationKit: &v1.IntegrationKit{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - v1.IntegrationKitLayoutLabel: v1.IntegrationKitLayoutNativeSources, - }, - Namespace: "ns", - }, - }, - Catalog: &Catalog{ - traits: traitList, - }, - CamelCatalog: &camel.RuntimeCatalog{ - CamelCatalogSpec: v1.CamelCatalogSpec{ - Loaders: map[string]v1.CamelLoader{ - "java": { - Metadata: map[string]string{ - "native": boolean.TrueString, - "sources-required-at-build-time": boolean.TrueString, - }, - }, - }, - }, - }, - } - - vols := make([]corev1.Volume, 0) - mnts := make([]corev1.VolumeMount, 0) - - env.configureVolumesAndMounts(&vols, &mnts) - - assert.Len(t, vols, 2) - assert.Len(t, mnts, 2) - - v := findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm1" }) - assert.NotNil(t, v) - assert.NotNil(t, v.VolumeSource.ConfigMap) - assert.Len(t, v.VolumeSource.ConfigMap.Items, 1) - assert.Equal(t, "source1.xml", v.VolumeSource.ConfigMap.Items[0].Key) - - m := findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name }) - assert.NotNil(t, m) - - v = findVolume(vols, func(v corev1.Volume) bool { return v.ConfigMap.Name == "my-cm4" }) - assert.NotNil(t, v) - assert.NotNil(t, v.VolumeSource.ConfigMap) - assert.Len(t, v.VolumeSource.ConfigMap.Items, 1) - assert.Equal(t, "content", v.VolumeSource.ConfigMap.Items[0].Key) - - m = findVVolumeMount(mnts, func(m corev1.VolumeMount) bool { return m.Name == v.Name }) - assert.NotNil(t, m) -} - func TestOnlySomeTraitsInfluenceBuild(t *testing.T) { c := NewTraitTestCatalog() buildTraits := []string{"builder", "camel", "quarkus", "registry"} diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go index 2daff66310..edb3bb2716 100644 --- a/pkg/trait/trait_types.go +++ b/pkg/trait/trait_types.go @@ -20,7 +20,6 @@ package trait import ( "context" "fmt" - "path" "path/filepath" "regexp" "sort" @@ -30,7 +29,6 @@ import ( batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" serving "knative.dev/serving/pkg/apis/serving/v1" @@ -38,11 +36,9 @@ import ( "github.com/apache/camel-k/v2/pkg/client" "github.com/apache/camel-k/v2/pkg/metadata" "github.com/apache/camel-k/v2/pkg/platform" - "github.com/apache/camel-k/v2/pkg/util/boolean" "github.com/apache/camel-k/v2/pkg/util/camel" "github.com/apache/camel-k/v2/pkg/util/kubernetes" "github.com/apache/camel-k/v2/pkg/util/log" - "github.com/apache/camel-k/v2/pkg/util/property" ) const ( @@ -421,146 +417,6 @@ func (e *Environment) DetermineCatalogNamespace() string { return "" } -func (e *Environment) computeApplicationProperties() (*corev1.ConfigMap, error) { - // application properties - applicationProperties, err := property.EncodePropertyFile(e.ApplicationProperties) - if err != nil { - return nil, fmt.Errorf("could not compute application properties: %w", err) - } - - if applicationProperties != "" { - return &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: e.Integration.Name + "-application-properties", - Namespace: e.Integration.Namespace, - Labels: map[string]string{ - v1.IntegrationLabel: e.Integration.Name, - "camel.apache.org/properties.type": "application", - kubernetes.ConfigMapTypeLabel: CamelPropertiesType, - }, - }, - Data: map[string]string{ - "application.properties": applicationProperties, - }, - }, nil - } - - return nil, nil -} - -func (e *Environment) addSourcesProperties() { - if e.ApplicationProperties == nil { - e.ApplicationProperties = make(map[string]string) - } - idx := 0 - for _, s := range e.Integration.AllSources() { - // We don't process routes embedded (native) or Kamelets - if e.isEmbedded(s) || s.IsGeneratedFromKamelet() { - continue - } - srcName := strings.TrimPrefix(filepath.ToSlash(s.Name), "/") - src := "file:" + path.Join(filepath.ToSlash(camel.SourcesMountPath), srcName) - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].location", idx)] = src - - simpleName := srcName - if strings.Contains(srcName, ".") { - simpleName = srcName[0:strings.Index(srcName, ".")] - } - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].name", idx)] = simpleName - - for pid, p := range s.PropertyNames { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].property-names[%d]", idx, pid)] = p - } - - if s.Type != "" { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].type", idx)] = string(s.Type) - } - if s.InferLanguage() != "" { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].language", idx)] = string(s.InferLanguage()) - } - if s.Loader != "" { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].loader", idx)] = s.Loader - } - if s.Compression { - e.ApplicationProperties[fmt.Sprintf("camel.k.sources[%d].compressed", idx)] = boolean.TrueString - } - - idx++ - } -} - -func (e *Environment) configureVolumesAndMounts(vols *[]corev1.Volume, mnts *[]corev1.VolumeMount) { - // Sources - idx := 0 - for _, s := range e.Integration.AllSources() { - // We don't process routes embedded (native) or Kamelets - if e.isEmbedded(s) || s.IsGeneratedFromKamelet() { - continue - } - // Routes are copied under /etc/camel/sources and discovered by the runtime accordingly - cmName := fmt.Sprintf("%s-source-%03d", e.Integration.Name, idx) - if s.ContentRef != "" { - cmName = s.ContentRef - } - cmKey := "content" - if s.ContentKey != "" { - cmKey = s.ContentKey - } - resName := strings.TrimPrefix(s.Name, "/") - refName := fmt.Sprintf("i-source-%03d", idx) - resPath := filepath.Join(camel.SourcesMountPath, resName) - vol := getVolume(refName, "configmap", cmName, cmKey, resName) - mnt := getMount(refName, resPath, resName, true) - - *vols = append(*vols, *vol) - *mnts = append(*mnts, *mnt) - idx++ - } - // Resources (likely application properties or kamelets) - if e.Resources != nil { - e.Resources.VisitConfigMap(func(configMap *corev1.ConfigMap) { - switch configMap.Labels[kubernetes.ConfigMapTypeLabel] { - case CamelPropertiesType: - // Camel properties - propertiesType := configMap.Labels["camel.apache.org/properties.type"] - resName := propertiesType + ".properties" - - var mountPath string - switch propertiesType { - case "application": - mountPath = filepath.Join(camel.BasePath, resName) - case "user": - mountPath = filepath.Join(camel.ConfDPath, resName) - } - - if propertiesType != "" { - refName := propertiesType + "-properties" - vol := getVolume(refName, "configmap", configMap.Name, "application.properties", resName) - mnt := getMount(refName, mountPath, resName, true) - - *vols = append(*vols, *vol) - *mnts = append(*mnts, *mnt) - } else { - log.WithValues("Function", "trait.configureVolumesAndMounts").Infof("Warning: could not determine camel properties type %s", propertiesType) - } - case KameletBundleType: - // Kamelets bundle configmap - kameletMountPoint := configMap.Annotations[kameletMountPointAnnotation] - refName := KameletBundleType - vol := getVolume(refName, "configmap", configMap.Name, "", "") - mnt := getMount(refName, kameletMountPoint, "", true) - - *vols = append(*vols, *vol) - *mnts = append(*mnts, *mnt) - } - }) - } -} - func getVolume(volName, storageType, storageName, filterKey, filterValue string) *corev1.Volume { items := convertToKeyToPath(filterKey, filterValue) volume := corev1.Volume{ @@ -782,3 +638,11 @@ func (e *Environment) consumeSourcesMeta( return consumeMeta(meta), nil } + +func (e *Environment) appendCloudPropertiesLocation(cloudPropertiesLocation string) { + if e.ApplicationProperties["camel.main.cloud-properties-location"] == "" { + e.ApplicationProperties["camel.main.cloud-properties-location"] = cloudPropertiesLocation + } else { + e.ApplicationProperties["camel.main.cloud-properties-location"] += "," + cloudPropertiesLocation + } +} diff --git a/pkg/trait/trait_types_test.go b/pkg/trait/trait_types_test.go index 608fc3d076..e08221432f 100644 --- a/pkg/trait/trait_types_test.go +++ b/pkg/trait/trait_types_test.go @@ -34,19 +34,6 @@ import ( "github.com/apache/camel-k/v2/pkg/util/test" ) -func TestMultilinePropertiesHandled(t *testing.T) { - e := Environment{ - ApplicationProperties: map[string]string{ - "prop": "multi\nline", - }, - Integration: &v1.Integration{}, - } - cm, err := e.computeApplicationProperties() - require.NoError(t, err) - assert.NotNil(t, cm) - assert.Equal(t, "prop = multi\\nline\n", cm.Data["application.properties"]) -} - func TestCollectConfigurationValues(t *testing.T) { e := Environment{ Integration: &v1.Integration{ diff --git a/pkg/util/kubernetes/lookup.go b/pkg/util/kubernetes/lookup.go index 2ce961d5b1..3fb2d0256c 100644 --- a/pkg/util/kubernetes/lookup.go +++ b/pkg/util/kubernetes/lookup.go @@ -29,6 +29,7 @@ import ( ) // LookupConfigmap will look for any k8s Configmap with a given name in a given namespace. +// Deprecated: won't be supported in future releases. func LookupConfigmap(ctx context.Context, c client.Client, ns string, name string) *corev1.ConfigMap { cm := corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ @@ -62,6 +63,7 @@ func LookupResourceVersion(ctx context.Context, c client.Client, object ctrl.Obj } // LookupSecret will look for any k8s Secret with a given name in a given namespace. +// Deprecated: won't be supported in future releases. func LookupSecret(ctx context.Context, c client.Client, ns string, name string) *corev1.Secret { secret := corev1.Secret{ TypeMeta: metav1.TypeMeta{