This is an automated email from the ASF dual-hosted git repository.

pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 2568fad4c43d047e15229106ea4934986be326ec
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Tue Dec 28 15:12:00 2021 +0100

    feat(trait/mount): moving volumes logic
---
 docs/modules/ROOT/nav.adoc               |   1 +
 docs/modules/traits/pages/container.adoc |  12 ---
 docs/modules/traits/pages/mount.adoc     |  45 ++++++++
 pkg/cmd/run.go                           |   4 +-
 pkg/trait/container.go                   |  77 -------------
 pkg/trait/mount.go                       | 177 ++++++++++++++++++++++++++++++
 pkg/trait/mount_test.go                  | 179 +++++++++++++++++++++++++++++++
 pkg/trait/trait_register.go              |   3 +-
 pkg/trait/trait_test.go                  |   2 +-
 resources/traits.yaml                    |  39 ++++---
 10 files changed, 433 insertions(+), 106 deletions(-)

diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 5bc1a65..593ab84 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -67,6 +67,7 @@
 ** xref:traits:knative.adoc[Knative]
 ** xref:traits:logging.adoc[Logging]
 ** xref:traits:master.adoc[Master]
+** xref:traits:mount.adoc[Mount]
 ** xref:traits:openapi.adoc[Openapi]
 ** xref:traits:owner.adoc[Owner]
 ** xref:traits:pdb.adoc[Pdb]
diff --git a/docs/modules/traits/pages/container.adoc 
b/docs/modules/traits/pages/container.adoc
index 0d608e4..f5be96f 100755
--- a/docs/modules/traits/pages/container.adoc
+++ b/docs/modules/traits/pages/container.adoc
@@ -81,18 +81,6 @@ The following configuration options are available:
 | PullPolicy
 | The pull policy: Always\|Never\|IfNotPresent
 
-| container.configs
-| []string
-| A list of configuration pointing to configmap/secret. Syntax: 
[configmap\|secret]:name[key], where name represents the resource name and key 
optionally represents the resource key to be filtered
-
-| container.resources
-| []string
-| A list of resources pointing to configmap/secret. Syntax: 
[configmap\|secret]:name[/key][@path], where name represents the resource name, 
key optionally represents the resource key to be filtered and path represents 
the destination path
-
-| container.volumes
-| []string
-| A list of Persistent Volume Claims to be mounted. Syntax: 
[pvcname:/container/path]
-
 | container.probes-enabled
 | bool
 | DeprecatedProbesEnabled enable/disable probes on the container (default 
`false`)
diff --git a/docs/modules/traits/pages/mount.adoc 
b/docs/modules/traits/pages/mount.adoc
new file mode 100644
index 0000000..d5e2c19
--- /dev/null
+++ b/docs/modules/traits/pages/mount.adoc
@@ -0,0 +1,45 @@
+= Mount Trait
+
+// Start of autogenerated code - DO NOT EDIT! (description)
+The Mount trait can be used to configure volumes mounted on the Integration 
Pod.
+
+nolint: tagliatelle
+
+This trait is available in the following profiles: **Kubernetes, Knative, 
OpenShift**.
+
+WARNING: The mount trait is a *platform trait*: disabling it may compromise 
the platform functionality.
+
+// End of autogenerated code - DO NOT EDIT! (description)
+// Start of autogenerated code - DO NOT EDIT! (configuration)
+== Configuration
+
+Trait properties can be specified when running any integration with the CLI:
+[source,console]
+----
+$ kamel run --trait mount.[key]=[value] --trait mount.[key2]=[value2] 
integration.groovy
+----
+The following configuration options are available:
+
+[cols="2m,1m,5a"]
+|===
+|Property | Type | Description
+
+| mount.enabled
+| bool
+| Can be used to enable or disable a trait. All traits share this common 
property.
+
+| mount.configs
+| []string
+| A list of configuration pointing to configmap/secret. Syntax: 
[configmap\|secret]:name[key], where name represents the resource name and key 
optionally represents the resource key to be filtered
+
+| mount.resources
+| []string
+| A list of resources pointing to configmap/secret. Syntax: 
[configmap\|secret]:name[/key][@path], where name represents the resource name, 
key optionally represents the resource key to be filtered and path represents 
the destination path
+
+| mount.volumes
+| []string
+| A list of Persistent Volume Claims to be mounted. Syntax: 
[pvcname:/container/path]
+
+|===
+
+// End of autogenerated code - DO NOT EDIT! (configuration)
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 1c1b183..35f2c0a 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -576,12 +576,12 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd 
*cobra.Command, c client.C
        }
 
        generatedConfigmaps := make([]*corev1.ConfigMap, 0)
-       resCms, err := o.parseAndConvertToTrait(c, integration, o.Resources, 
resource.ParseResource, func(c *resource.Config) string { return c.String() }, 
"container.resources")
+       resCms, err := o.parseAndConvertToTrait(c, integration, o.Resources, 
resource.ParseResource, func(c *resource.Config) string { return c.String() }, 
"mount.resources")
        if err != nil {
                return nil, err
        }
        generatedConfigmaps = append(generatedConfigmaps, resCms...)
-       confCms, err := o.parseAndConvertToTrait(c, integration, o.Configs, 
resource.ParseConfig, func(c *resource.Config) string { return c.String() }, 
"container.configs")
+       confCms, err := o.parseAndConvertToTrait(c, integration, o.Configs, 
resource.ParseConfig, func(c *resource.Config) string { return c.String() }, 
"mount.configs")
        if err != nil {
                return nil, err
        }
diff --git a/pkg/trait/container.go b/pkg/trait/container.go
index 0abf914..5fc7da8 100644
--- a/pkg/trait/container.go
+++ b/pkg/trait/container.go
@@ -20,7 +20,6 @@ package trait
 import (
        "fmt"
        "path"
-       "strings"
 
        appsv1 "k8s.io/api/apps/v1"
        "k8s.io/api/batch/v1beta1"
@@ -36,7 +35,6 @@ import (
        "github.com/apache/camel-k/pkg/util/defaults"
        "github.com/apache/camel-k/pkg/util/envvar"
        "github.com/apache/camel-k/pkg/util/kubernetes"
-       utilResource "github.com/apache/camel-k/pkg/util/resource"
 )
 
 const (
@@ -83,12 +81,6 @@ type containerTrait struct {
        Image string `property:"image" json:"image,omitempty"`
        // The pull policy: Always|Never|IfNotPresent
        ImagePullPolicy corev1.PullPolicy `property:"image-pull-policy" 
json:"imagePullPolicy,omitempty"`
-       // A list of configuration pointing to configmap/secret. Syntax: 
[configmap|secret]:name[key], where name represents the resource name and key 
optionally represents the resource key to be filtered
-       Configs []string `property:"configs" json:"configs,omitempty"`
-       // A list of resources pointing to configmap/secret. Syntax: 
[configmap|secret]:name[/key][@path], where name represents the resource name, 
key optionally represents the resource key to be filtered and path represents 
the destination path
-       Resources []string `property:"resources" json:"resources,omitempty"`
-       // A list of Persistent Volume Claims to be mounted. Syntax: 
[pvcname:/container/path]
-       Volumes []string `property:"volumes" json:"volumes,omitempty"`
 
        // DeprecatedProbesEnabled enable/disable probes on the container 
(default `false`)
        // Deprecated: replaced by the health trait.
@@ -168,23 +160,6 @@ func (t *containerTrait) Configure(e *Environment) (bool, 
error) {
                return false, fmt.Errorf("unsupported pull policy %s", 
t.ImagePullPolicy)
        }
 
-       // Validate resources and pvcs
-       for _, c := range t.Configs {
-               if !strings.HasPrefix(c, "configmap:") && !strings.HasPrefix(c, 
"secret:") {
-                       return false, fmt.Errorf("unsupported config %s, must 
be a configmap or secret resource", c)
-               }
-       }
-       for _, r := range t.Resources {
-               if !strings.HasPrefix(r, "configmap:") && !strings.HasPrefix(r, 
"secret:") {
-                       return false, fmt.Errorf("unsupported resource %s, must 
be a configmap or secret resource", r)
-               }
-       }
-       for _, r := range t.Volumes {
-               if !strings.HasPrefix(r, "pvc:") {
-                       return false, fmt.Errorf("unsupported volume %s, must 
be a pvc", r)
-               }
-       }
-
        return true, nil
 }
 
@@ -287,7 +262,6 @@ func (t *containerTrait) configureContainer(e *Environment) 
error {
        }
        t.configureCapabilities(e)
 
-       var volumes *[]corev1.Volume
        var containers *[]corev1.Container
        visited := false
 
@@ -297,7 +271,6 @@ func (t *containerTrait) configureContainer(e *Environment) 
error {
                        envvar.SetVar(&container.Env, envVar)
                }
 
-               volumes = &deployment.Spec.Template.Spec.Volumes
                containers = &deployment.Spec.Template.Spec.Containers
                visited = true
                return nil
@@ -322,7 +295,6 @@ func (t *containerTrait) configureContainer(e *Environment) 
error {
                        }
                }
 
-               volumes = &service.Spec.ConfigurationSpec.Template.Spec.Volumes
                containers = 
&service.Spec.ConfigurationSpec.Template.Spec.Containers
                visited = true
                return nil
@@ -336,7 +308,6 @@ func (t *containerTrait) configureContainer(e *Environment) 
error {
                        envvar.SetVar(&container.Env, envVar)
                }
 
-               volumes = &cron.Spec.JobTemplate.Spec.Template.Spec.Volumes
                containers = 
&cron.Spec.JobTemplate.Spec.Template.Spec.Containers
                visited = true
                return nil
@@ -345,60 +316,12 @@ func (t *containerTrait) configureContainer(e 
*Environment) error {
        }
 
        if visited {
-               // Volumes declared in the Integration resources
-               e.configureVolumesAndMounts(volumes, &container.VolumeMounts)
-               // Volumes declared in the trait config/resource options
-               err := t.configureVolumesAndMounts(volumes, 
&container.VolumeMounts)
-               if err != nil {
-                       return err
-               }
                *containers = append(*containers, container)
        }
 
        return nil
 }
 
-func (t *containerTrait) configureVolumesAndMounts(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)
-               } else {
-                       return parseErr
-               }
-       }
-       for _, r := range t.Resources {
-               if res, parseErr := utilResource.ParseResource(r); parseErr == 
nil {
-                       t.mountResource(vols, mnts, res)
-               } else {
-                       return parseErr
-               }
-       }
-       for _, v := range t.Volumes {
-               if vol, parseErr := utilResource.ParseVolume(v); parseErr == 
nil {
-                       t.mountResource(vols, mnts, vol)
-               } else {
-                       return parseErr
-               }
-       }
-
-       return nil
-}
-
-func (t *containerTrait) mountResource(vols *[]corev1.Volume, mnts 
*[]corev1.VolumeMount, conf *utilResource.Config) {
-       refName := kubernetes.SanitizeLabel(conf.Name())
-       vol := getVolume(refName, string(conf.StorageType()), conf.Name(), 
conf.Key(), conf.Key())
-       mntPath := getMountPoint(conf.Name(), conf.DestinationPath(), 
string(conf.StorageType()), string(conf.ContentType()))
-       readOnly := true
-       if conf.StorageType() == utilResource.StorageTypePVC {
-               readOnly = false
-       }
-       // No need to specify a subpath, as we mount the entire configmap/secret
-       mnt := getMount(refName, mntPath, "", readOnly)
-
-       *vols = append(*vols, *vol)
-       *mnts = append(*mnts, *mnt)
-}
-
 func (t *containerTrait) configureService(e *Environment, container 
*corev1.Container) {
        service := e.Resources.GetServiceForIntegration(e.Integration)
        if service == nil {
diff --git a/pkg/trait/mount.go b/pkg/trait/mount.go
new file mode 100644
index 0000000..18114bb
--- /dev/null
+++ b/pkg/trait/mount.go
@@ -0,0 +1,177 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package trait
+
+import (
+       "fmt"
+       "strings"
+
+       appsv1 "k8s.io/api/apps/v1"
+       "k8s.io/api/batch/v1beta1"
+       corev1 "k8s.io/api/core/v1"
+
+       serving "knative.dev/serving/pkg/apis/serving/v1"
+
+       v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       utilResource "github.com/apache/camel-k/pkg/util/resource"
+)
+
+// The Mount trait can be used to configure volumes mounted on the Integration 
Pod.
+//
+// +camel-k:trait=mount
+// nolint: tagliatelle
+type mountTrait struct {
+       BaseTrait `property:",squash"`
+       // A list of configuration pointing to configmap/secret. Syntax: 
[configmap|secret]:name[key], where name represents the resource name and key 
optionally represents the resource key to be filtered
+       Configs []string `property:"configs" json:"configs,omitempty"`
+       // A list of resources pointing to configmap/secret. Syntax: 
[configmap|secret]:name[/key][@path], where name represents the resource name, 
key optionally represents the resource key to be filtered and path represents 
the destination path
+       Resources []string `property:"resources" json:"resources,omitempty"`
+       // A list of Persistent Volume Claims to be mounted. Syntax: 
[pvcname:/container/path]
+       Volumes []string `property:"volumes" json:"volumes,omitempty"`
+}
+
+func newMountTrait() Trait {
+       return &mountTrait{
+               // Must follow immediately the container trait
+               BaseTrait: NewBaseTrait("mount", 1610),
+       }
+}
+
+func (t *mountTrait) Configure(e *Environment) (bool, error) {
+       if IsFalse(t.Enabled) {
+               return false, nil
+       }
+
+       if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && 
!e.IntegrationInRunningPhases() {
+               return false, nil
+       }
+
+       // Validate resources and pvcs
+       for _, c := range t.Configs {
+               if !strings.HasPrefix(c, "configmap:") && !strings.HasPrefix(c, 
"secret:") {
+                       return false, fmt.Errorf("unsupported config %s, must 
be a configmap or secret resource", c)
+               }
+       }
+       for _, r := range t.Resources {
+               if !strings.HasPrefix(r, "configmap:") && !strings.HasPrefix(r, 
"secret:") {
+                       return false, fmt.Errorf("unsupported resource %s, must 
be a configmap or secret resource", r)
+               }
+       }
+
+       return true, nil
+}
+
+func (t *mountTrait) Apply(e *Environment) error {
+       if e.IntegrationInPhase(v1.IntegrationPhaseInitialization) {
+               return nil
+       }
+
+       container := e.GetIntegrationContainer()
+       if container == nil {
+               return fmt.Errorf("unable to find integration container: %s", 
e.Integration.Name)
+       }
+
+       var volumes *[]corev1.Volume
+       visited := false
+
+       // Deployment
+       if err := e.Resources.VisitDeploymentE(func(deployment 
*appsv1.Deployment) error {
+               volumes = &deployment.Spec.Template.Spec.Volumes
+               visited = true
+               return nil
+       }); err != nil {
+               return err
+       }
+
+       // Knative Service
+       if err := e.Resources.VisitKnativeServiceE(func(service 
*serving.Service) error {
+               volumes = &service.Spec.ConfigurationSpec.Template.Spec.Volumes
+               visited = true
+               return nil
+       }); err != nil {
+               return err
+       }
+
+       // CronJob
+       if err := e.Resources.VisitCronJobE(func(cron *v1beta1.CronJob) error {
+               volumes = &cron.Spec.JobTemplate.Spec.Template.Spec.Volumes
+               visited = true
+               return nil
+       }); err != nil {
+               return err
+       }
+
+       if visited {
+               // Volumes declared in the Integration resources
+               e.configureVolumesAndMounts(volumes, &container.VolumeMounts)
+               // Volumes declared in the trait config/resource options
+               err := t.configureVolumesAndMounts(volumes, 
&container.VolumeMounts)
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+func (t *mountTrait) configureVolumesAndMounts(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)
+               } else {
+                       return parseErr
+               }
+       }
+       for _, r := range t.Resources {
+               if res, parseErr := utilResource.ParseResource(r); parseErr == 
nil {
+                       t.mountResource(vols, mnts, res)
+               } else {
+                       return parseErr
+               }
+       }
+       for _, v := range t.Volumes {
+               if vol, parseErr := utilResource.ParseVolume(v); parseErr == 
nil {
+                       t.mountResource(vols, mnts, vol)
+               } else {
+                       return parseErr
+               }
+       }
+
+       return nil
+}
+
+func (t *mountTrait) mountResource(vols *[]corev1.Volume, mnts 
*[]corev1.VolumeMount, conf *utilResource.Config) {
+       refName := kubernetes.SanitizeLabel(conf.Name())
+       vol := getVolume(refName, string(conf.StorageType()), conf.Name(), 
conf.Key(), conf.Key())
+       mntPath := getMountPoint(conf.Name(), conf.DestinationPath(), 
string(conf.StorageType()), string(conf.ContentType()))
+       readOnly := true
+       if conf.StorageType() == utilResource.StorageTypePVC {
+               readOnly = false
+       }
+       // No need to specify a subpath, as we mount the entire configmap/secret
+       mnt := getMount(refName, mntPath, "", readOnly)
+
+       *vols = append(*vols, *vol)
+       *mnts = append(*mnts, *mnt)
+}
+
+// IsPlatformTrait overrides base class method.
+func (t *mountTrait) IsPlatformTrait() bool {
+       return true
+}
diff --git a/pkg/trait/mount_test.go b/pkg/trait/mount_test.go
new file mode 100644
index 0000000..eee4bc0
--- /dev/null
+++ b/pkg/trait/mount_test.go
@@ -0,0 +1,179 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package trait
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+
+       appsv1 "k8s.io/api/apps/v1"
+       corev1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+       v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/pkg/util/camel"
+       "github.com/apache/camel-k/pkg/util/gzip"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       "github.com/apache/camel-k/pkg/util/test"
+)
+
+func TestMountVolumesEmpty(t *testing.T) {
+       traitCatalog := NewCatalog(nil)
+
+       environment := getNominalEnv(t, traitCatalog)
+       environment.Integration.Spec.Traits = map[string]v1.TraitSpec{}
+       environment.Platform.ResyncStatusFullConfig()
+
+       err := traitCatalog.apply(environment)
+
+       assert.Nil(t, err)
+       assert.NotEmpty(t, environment.ExecutedTraits)
+       assert.NotNil(t, environment.GetTrait("mount"))
+
+       s := environment.Resources.GetDeployment(func(service 
*appsv1.Deployment) bool {
+               return service.Name == "hello"
+       })
+       assert.NotNil(t, s)
+       spec := s.Spec.Template.Spec
+
+       assert.Len(t, spec.Containers[0].VolumeMounts, 2)
+       assert.Len(t, spec.Volumes, 2)
+}
+
+func TestMountVolumesIntegrationPhaseDeploying(t *testing.T) {
+       traitCatalog := NewCatalog(nil)
+
+       environment := getNominalEnv(t, traitCatalog)
+       environment.Platform.ResyncStatusFullConfig()
+
+       err := traitCatalog.apply(environment)
+
+       assert.Nil(t, err)
+       assert.NotEmpty(t, environment.ExecutedTraits)
+       assert.NotNil(t, environment.GetTrait("mount"))
+
+       s := environment.Resources.GetDeployment(func(service 
*appsv1.Deployment) bool {
+               return service.Name == "hello"
+       })
+       assert.NotNil(t, s)
+       spec := s.Spec.Template.Spec
+
+       assert.Len(t, spec.Containers[0].VolumeMounts, 5)
+       assert.Len(t, spec.Volumes, 5)
+
+       assert.Condition(t, func() bool {
+               for _, v := range spec.Containers[0].VolumeMounts {
+                       if v.Name == "my-pvc" {
+                               return true
+                       }
+               }
+               return false
+       })
+       assert.Condition(t, func() bool {
+               for _, v := range spec.Containers[0].VolumeMounts {
+                       if v.Name == "my-cm" {
+                               return true
+                       }
+               }
+               return false
+       })
+       assert.Condition(t, func() bool {
+               for _, v := range spec.Volumes {
+                       if v.Name == "my-secret" {
+                               return true
+                       }
+               }
+               return false
+       })
+}
+
+func TestMountVolumesIntegrationPhaseInitialization(t *testing.T) {
+       traitCatalog := NewCatalog(nil)
+
+       environment := getNominalEnv(t, traitCatalog)
+       environment.Integration.Status.Phase = v1.IntegrationPhaseInitialization
+       environment.Platform.ResyncStatusFullConfig()
+
+       err := traitCatalog.apply(environment)
+
+       assert.Nil(t, err)
+       assert.NotEmpty(t, environment.ExecutedTraits)
+       assert.NotNil(t, environment.GetTrait("mount"))
+
+       s := environment.Resources.GetDeployment(func(service 
*appsv1.Deployment) bool {
+               return service.Name == "hello"
+       })
+       assert.Nil(t, s)
+}
+
+func getNominalEnv(t *testing.T, traitCatalog *Catalog) *Environment {
+       t.Helper()
+       catalog, _ := camel.DefaultCatalog()
+       compressedRoute, _ := 
gzip.CompressBase64([]byte(`from("undertow:test").log("hello")`))
+
+       return &Environment{
+               CamelCatalog: catalog,
+               Catalog:      traitCatalog,
+               Integration: &v1.Integration{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name:      "hello",
+                               Namespace: "default",
+                       },
+                       Status: v1.IntegrationStatus{
+                               Phase: v1.IntegrationPhaseDeploying,
+                       },
+                       Spec: v1.IntegrationSpec{
+                               Sources: []v1.SourceSpec{
+                                       {
+                                               DataSpec: v1.DataSpec{
+                                                       Name:        
"routes.js",
+                                                       Content:     
string(compressedRoute),
+                                                       Compression: true,
+                                               },
+                                               Language: v1.LanguageJavaScript,
+                                       },
+                               },
+                               Traits: map[string]v1.TraitSpec{
+                                       "mount": test.TraitSpecFromMap(t, 
map[string]interface{}{
+                                               "configs":   
[]string{"configmap:my-cm"},
+                                               "resources": 
[]string{"secret:my-secret"},
+                                               "volumes":   
[]string{"my-pvc:/over/the/rainbow"},
+                                       }),
+                               },
+                       },
+               },
+               IntegrationKit: &v1.IntegrationKit{
+                       Status: v1.IntegrationKitStatus{
+                               Phase: v1.IntegrationKitPhaseReady,
+                       },
+               },
+               Platform: &v1.IntegrationPlatform{
+                       Spec: v1.IntegrationPlatformSpec{
+                               Cluster: v1.IntegrationPlatformClusterOpenShift,
+                               Build: v1.IntegrationPlatformBuildSpec{
+                                       PublishStrategy: 
v1.IntegrationPlatformBuildPublishStrategyS2I,
+                                       Registry:        
v1.RegistrySpec{Address: "registry"},
+                               },
+                       },
+               },
+               EnvVars:        make([]corev1.EnvVar, 0),
+               ExecutedTraits: make([]Trait, 0),
+               Resources:      kubernetes.NewCollection(),
+       }
+}
diff --git a/pkg/trait/trait_register.go b/pkg/trait/trait_register.go
index 68d2997..4f2a6d1 100644
--- a/pkg/trait/trait_register.go
+++ b/pkg/trait/trait_register.go
@@ -32,6 +32,7 @@ func init() {
        AddToTraits(newErrorHandlerTrait)
        AddToTraits(newGarbageCollectorTrait)
        AddToTraits(newHealthTrait)
+       AddToTraits(newInitTrait)
        AddToTraits(newIngressTrait)
        AddToTraits(newIstioTrait)
        AddToTraits(newJolokiaTrait)
@@ -40,7 +41,7 @@ func init() {
        AddToTraits(newKnativeTrait)
        AddToTraits(newKnativeServiceTrait)
        AddToTraits(newLoggingTraitTrait)
-       AddToTraits(newInitTrait)
+       AddToTraits(newMountTrait)
        AddToTraits(newOpenAPITrait)
        AddToTraits(newOwnerTrait)
        AddToTraits(newPdbTrait)
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index bef6932..0bc1f41 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -495,7 +495,7 @@ func TestOnlySomeTraitsInfluenceBuild(t *testing.T) {
 func TestOnlySomeTraitsArePlatform(t *testing.T) {
        c := NewTraitTestCatalog()
        platformTraits := []string{
-               "builder", "camel", "jvm", "runtime", "container", 
"dependencies", "deployer",
+               "builder", "camel", "jvm", "runtime", "container", "mount", 
"dependencies", "deployer",
                "deployment", "environment", "error-handler", "kamelets", 
"openapi", "owner", "platform", "quarkus",
        }
 
diff --git a/resources/traits.yaml b/resources/traits.yaml
index d3ec499..2b66ec4 100755
--- a/resources/traits.yaml
+++ b/resources/traits.yaml
@@ -132,19 +132,6 @@ traits:
   - name: image-pull-policy
     type: PullPolicy
     description: 'The pull policy: Always|Never|IfNotPresent'
-  - name: configs
-    type: '[]string'
-    description: 'A list of configuration pointing to configmap/secret. 
Syntax: [configmap|secret]:name[key],
-      where name represents the resource name and key optionally represents 
the resource
-      key to be filtered'
-  - name: resources
-    type: '[]string'
-    description: 'A list of resources pointing to configmap/secret. Syntax: 
[configmap|secret]:name[/key][@path],
-      where name represents the resource name, key optionally represents the 
resource
-      key to be filtered and path represents the destination path'
-  - name: volumes
-    type: '[]string'
-    description: 'A list of Persistent Volume Claims to be mounted. Syntax: 
[pvcname:/container/path]'
   - name: probes-enabled
     type: bool
     description: 'DeprecatedProbesEnabled enable/disable probes on the 
container (default
@@ -765,6 +752,32 @@ traits:
     type: string
     description: Label value that will be used to identify all pods contending 
the
       lock. Defaults to the integration name.
+- name: mount
+  platform: true
+  profiles:
+  - Kubernetes
+  - Knative
+  - OpenShift
+  description: 'The Mount trait can be used to configure volumes mounted on 
the Integration
+    Pod. nolint: tagliatelle'
+  properties:
+  - name: enabled
+    type: bool
+    description: Can be used to enable or disable a trait. All traits share 
this common
+      property.
+  - name: configs
+    type: '[]string'
+    description: 'A list of configuration pointing to configmap/secret. 
Syntax: [configmap|secret]:name[key],
+      where name represents the resource name and key optionally represents 
the resource
+      key to be filtered'
+  - name: resources
+    type: '[]string'
+    description: 'A list of resources pointing to configmap/secret. Syntax: 
[configmap|secret]:name[/key][@path],
+      where name represents the resource name, key optionally represents the 
resource
+      key to be filtered and path represents the destination path'
+  - name: volumes
+    type: '[]string'
+    description: 'A list of Persistent Volume Claims to be mounted. Syntax: 
[pvcname:/container/path]'
 - name: openapi
   platform: true
   profiles:

Reply via email to