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

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

commit 924b98df064a137da1487bdbe74b74445cb881e4
Author: Pasquale Congiusti <pasquale.congiu...@gmail.com>
AuthorDate: Thu Jul 22 10:20:42 2021 +0200

    feat(trait): configuration base trait
    
    Moving the logic previously defined in deployment, cron and knative service 
into a configuration trait
    
    Ref #2320
---
 pkg/cmd/run.go                  |  60 ++++++++++++----------
 pkg/cmd/run_test.go             |  22 +++-----
 pkg/trait/configuration.go      | 108 ++++++++++++++++++++++++++++++++++++++++
 pkg/trait/configuration_test.go |  98 ++++++++++++++++++++++++++++++++++++
 pkg/trait/cron.go               |   3 --
 pkg/trait/deployment.go         |   3 --
 pkg/trait/deployment_test.go    |  14 +-----
 pkg/trait/knative_service.go    |   3 --
 pkg/trait/trait_register.go     |  46 +++++++++--------
 pkg/trait/trait_test.go         |   3 +-
 10 files changed, 275 insertions(+), 85 deletions(-)

diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 45e1ce2..52971bd 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -580,8 +580,13 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd 
*cobra.Command, c client.C
        if err != nil {
                return nil, err
        }
-       if err := addIntegrationProperties(props, &integration.Spec); err != 
nil {
-               return nil, err
+       for _, key := range props.Keys() {
+               kv := fmt.Sprintf("%s=%s", key, props.GetString(key, ""))
+               if propsTraits, err := convertToTraitParameter(kv, 
"configuration.properties"); err != nil {
+                       return nil, err
+               } else {
+                       o.Traits = append(o.Traits, propsTraits...)
+               }
        }
 
        // convert each build configuration to a builder trait property
@@ -589,16 +594,12 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd 
*cobra.Command, c client.C
        if err != nil {
                return nil, err
        }
-       for _, k := range buildProps.Keys() {
-               v, ok := buildProps.Get(k)
-               if ok {
-                       entry, err := property.EncodePropertyFileEntry(k, v)
-                       if err != nil {
-                               return nil, err
-                       }
-                       o.Traits = append(o.Traits, 
fmt.Sprintf("builder.properties=%s", entry))
-               } else {
+       for _, key := range buildProps.Keys() {
+               kv := fmt.Sprintf("%s=%s", key, buildProps.GetString(key, ""))
+               if buildPropsTraits, err := convertToTraitParameter(kv, 
"builder.properties"); err != nil {
                        return nil, err
+               } else {
+                       o.Traits = append(o.Traits, buildPropsTraits...)
                }
        }
 
@@ -686,6 +687,28 @@ func addResource(resourceLocation string, integrationSpec 
*v1.IntegrationSpec, e
        return nil
 }
 
+func convertToTraitParameter(value, traitParameter string) ([]string, error) {
+       traits := make([]string, 0)
+       props, err := extractProperties(value)
+       if err != nil {
+               return nil, err
+       }
+       for _, k := range props.Keys() {
+               v, ok := props.Get(k)
+               if ok {
+                       entry, err := property.EncodePropertyFileEntry(k, v)
+                       if err != nil {
+                               return nil, err
+                       }
+                       traits = append(traits, fmt.Sprintf("%s=%s", 
traitParameter, entry))
+               } else {
+                       return nil, err
+               }
+       }
+
+       return traits, nil
+}
+
 func (o *runCmdOptions) GetIntegrationName(sources []string) string {
        name := ""
        if o.IntegrationName != "" {
@@ -713,21 +736,6 @@ func (o *runCmdOptions) configureTraits(integration 
*v1.Integration, options []s
        return nil
 }
 
-func addIntegrationProperties(props *properties.Properties, spec 
*v1.IntegrationSpec) error {
-       for _, k := range props.Keys() {
-               v, _ := props.Get(k)
-               entry, err := property.EncodePropertyFileEntry(k, v)
-               if err != nil {
-                       return err
-               }
-               spec.AddConfiguration(
-                       "property",
-                       entry,
-               )
-       }
-       return nil
-}
-
 func loadPropertyFile(fileName string) (*properties.Properties, error) {
        file, err := ioutil.ReadFile(fileName)
        if err != nil {
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index bfe9ea6..c89d6f4 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -252,16 +252,12 @@ func TestAddPropertyFile(t *testing.T) {
        assert.Nil(t, tmpFile.Close())
        assert.Nil(t, ioutil.WriteFile(tmpFile.Name(), 
[]byte(TestPropertyFileContent), 0644))
 
-       spec := v1.IntegrationSpec{}
-       properties, err := extractProperties("file:" + tmpFile.Name())
+       properties, err := convertToTraitParameter("file:"+tmpFile.Name(), 
"trait.properties")
        assert.Nil(t, err)
-       assert.Nil(t, addIntegrationProperties(properties, &spec))
-       assert.Equal(t, 3, len(spec.Configuration))
-       assert.Equal(t, `a = b`, spec.Configuration[0].Value)
-       //assert.Equal(t, `c\=d = e`, spec.Configuration[1].Value)
-       //assert.Equal(t, `d = c\=e`, spec.Configuration[2].Value)
-       assert.Equal(t, `f = g:h`, spec.Configuration[1].Value)
-       assert.Equal(t, `i = j\nk`, spec.Configuration[2].Value)
+       assert.Equal(t, 3, len(properties))
+       assert.Equal(t, `trait.properties=a = b`, properties[0])
+       assert.Equal(t, `trait.properties=f = g:h`, properties[1])
+       assert.Equal(t, `trait.properties=i = j\nk`, properties[2])
 }
 
 func TestRunPropertyFileFlag(t *testing.T) {
@@ -284,12 +280,10 @@ func TestRunPropertyFileFlag(t *testing.T) {
 }
 
 func TestRunProperty(t *testing.T) {
-       spec := v1.IntegrationSpec{}
-       properties, err := extractProperties(`key=value\nnewline`)
+       properties, err := convertToTraitParameter(`key=value\nnewline`, 
"trait.properties")
        assert.Nil(t, err)
-       assert.Nil(t, addIntegrationProperties(properties, &spec))
-       assert.Equal(t, 1, len(spec.Configuration))
-       assert.Equal(t, `key = value\nnewline`, spec.Configuration[0].Value)
+       assert.Equal(t, 1, len(properties))
+       assert.Equal(t, `trait.properties=key = value\nnewline`, properties[0])
 }
 
 func TestRunResourceFlag(t *testing.T) {
diff --git a/pkg/trait/configuration.go b/pkg/trait/configuration.go
new file mode 100644
index 0000000..74c2f10
--- /dev/null
+++ b/pkg/trait/configuration.go
@@ -0,0 +1,108 @@
+/*
+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"
+
+       v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/pkg/util/property"
+       corev1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// The configuration trait is used to customize the Integration configuration 
such as properties and resources.
+//
+// +camel-k:trait=configuration
+type configurationTrait struct {
+       BaseTrait `property:",squash"`
+       // A list of properties to be provided to the Integration runtime
+       Properties []string `property:"properties" json:"properties,omitempty"`
+}
+
+func newConfigurationTrait() Trait {
+       return &configurationTrait{
+               BaseTrait: NewBaseTrait("configuration", 700),
+       }
+}
+
+func (t *configurationTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
+       }
+
+       return true, nil
+}
+
+func (t *configurationTrait) Apply(e *Environment) error {
+       if e.InPhase(v1.IntegrationKitPhaseReady, v1.IntegrationPhaseDeploying) 
||
+               e.InPhase(v1.IntegrationKitPhaseReady, 
v1.IntegrationPhaseRunning) {
+               // Get all resources
+               maps := e.computeConfigMaps()
+               if t.Properties != nil {
+                       // Only user.properties
+                       maps = append(maps, t.computeUserProperties(e)...)
+               }
+               e.Resources.AddAll(maps)
+       }
+
+       return nil
+}
+
+func (t *configurationTrait) IsPlatformTrait() bool {
+       return true
+}
+
+func (t *configurationTrait) computeUserProperties(e *Environment) 
[]ctrl.Object {
+       maps := make([]ctrl.Object, 0)
+
+       // combine properties of integration with kit, integration
+       // properties have the priority
+       userProperties := ""
+
+       for _, prop := range t.Properties {
+               k, v := property.SplitPropertyFileEntry(prop)
+               userProperties += fmt.Sprintf("%s=%s\n", k, v)
+       }
+
+       if userProperties != "" {
+               maps = append(
+                       maps,
+                       &corev1.ConfigMap{
+                               TypeMeta: metav1.TypeMeta{
+                                       Kind:       "ConfigMap",
+                                       APIVersion: "v1",
+                               },
+                               ObjectMeta: metav1.ObjectMeta{
+                                       Name:      e.Integration.Name + 
"-user-properties",
+                                       Namespace: e.Integration.Namespace,
+                                       Labels: map[string]string{
+                                               v1.IntegrationLabel:            
    e.Integration.Name,
+                                               
"camel.apache.org/properties.type": "user",
+                                       },
+                               },
+                               Data: map[string]string{
+                                       "application.properties": 
userProperties,
+                               },
+                       },
+               )
+       }
+
+       return maps
+}
diff --git a/pkg/trait/configuration_test.go b/pkg/trait/configuration_test.go
new file mode 100644
index 0000000..4e457c8
--- /dev/null
+++ b/pkg/trait/configuration_test.go
@@ -0,0 +1,98 @@
+/*
+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"
+
+       v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       "github.com/apache/camel-k/pkg/util/test"
+
+       "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"
+)
+
+func TestApplyConfigurationTraitWithProperties(t *testing.T) {
+       configurationTrait, environment := createNominalConfigurationTest()
+       configurationTrait.Properties = []string{"a=b", "c=d"}
+       err := configurationTrait.Apply(environment)
+       assert.Nil(t, err)
+
+       userPropertiesCm := environment.Resources.GetConfigMap(func(cm 
*corev1.ConfigMap) bool {
+               return cm.Labels["camel.apache.org/properties.type"] == "user"
+       })
+       assert.NotNil(t, userPropertiesCm)
+       assert.Equal(t, map[string]string{
+               "application.properties": "a=b\nc=d\n",
+       }, userPropertiesCm.Data)
+}
+
+func createNominalConfigurationTest() (*configurationTrait, *Environment) {
+       trait := newConfigurationTrait().(*configurationTrait)
+       trait.Enabled = BoolP(true)
+       trait.Client, _ = test.NewFakeClient(&appsv1.Deployment{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "integration-name",
+                       Namespace: "namespace",
+                       Labels: map[string]string{
+                               v1.IntegrationLabel: "integration-name",
+                       },
+               },
+       })
+
+       replicas := int32(3)
+
+       environment := &Environment{
+               Catalog: NewCatalog(nil),
+               Integration: &v1.Integration{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name: "integration-name",
+                       },
+                       Spec: v1.IntegrationSpec{
+                               Replicas: &replicas,
+                               Traits:   make(map[string]v1.TraitSpec),
+                       },
+                       Status: v1.IntegrationStatus{
+                               Phase: v1.IntegrationPhaseDeploying,
+                       },
+               },
+               IntegrationKit: &v1.IntegrationKit{
+                       Status: v1.IntegrationKitStatus{
+                               Phase: v1.IntegrationKitPhaseReady,
+                       },
+               },
+               Platform: &v1.IntegrationPlatform{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Namespace: "namespace",
+                       },
+                       Spec: v1.IntegrationPlatformSpec{
+                               Cluster: v1.IntegrationPlatformClusterOpenShift,
+                               Profile: v1.TraitProfileKnative,
+                       },
+               },
+               Resources:             kubernetes.NewCollection(),
+               ApplicationProperties: make(map[string]string),
+       }
+       environment.Platform.ResyncStatusFullConfig()
+
+       return trait, environment
+}
diff --git a/pkg/trait/cron.go b/pkg/trait/cron.go
index 9828a58..591758b 100644
--- a/pkg/trait/cron.go
+++ b/pkg/trait/cron.go
@@ -245,9 +245,6 @@ func (t *cronTrait) Apply(e *Environment) error {
                e.Interceptors = append(e.Interceptors, "cron")
 
                cronJob := t.getCronJobFor(e)
-               maps := e.computeConfigMaps()
-
-               e.Resources.AddAll(maps)
                e.Resources.Add(cronJob)
 
                e.Integration.Status.SetCondition(
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 997dd33..2460a5c 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -99,10 +99,7 @@ func (t *deploymentTrait) ControllerStrategySelectorOrder() 
int {
 }
 
 func (t *deploymentTrait) Apply(e *Environment) error {
-       maps := e.computeConfigMaps()
        deployment := t.getDeploymentFor(e)
-
-       e.Resources.AddAll(maps)
        e.Resources.Add(deployment)
 
        e.Integration.Status.SetCondition(
diff --git a/pkg/trait/deployment_test.go b/pkg/trait/deployment_test.go
index de571ca..efc8456 100644
--- a/pkg/trait/deployment_test.go
+++ b/pkg/trait/deployment_test.go
@@ -102,22 +102,9 @@ func 
TestApplyDeploymentTraitWhileResolvingKitDoesNotSucceed(t *testing.T) {
 
 func TestApplyDeploymentTraitWhileDeployingIntegrationDoesSucceed(t 
*testing.T) {
        deploymentTrait, environment := createNominalDeploymentTest()
-       environment.Integration.Spec.Configuration = 
append(environment.Integration.Spec.Configuration, v1.ConfigurationSpec{
-               Type:  "property",
-               Value: "a=b",
-       })
-
        err := deploymentTrait.Apply(environment)
-
        assert.Nil(t, err)
 
-       assert.NotNil(t, environment.Resources.GetConfigMap(func(cm 
*corev1.ConfigMap) bool {
-               return cm.Labels["camel.apache.org/properties.type"] == "user"
-       }))
-       assert.Nil(t, environment.Resources.GetConfigMap(func(cm 
*corev1.ConfigMap) bool {
-               return cm.Labels["camel.apache.org/properties.type"] == 
"application"
-       }))
-
        deployment := environment.Resources.GetDeployment(func(deployment 
*appsv1.Deployment) bool { return true })
        assert.NotNil(t, deployment)
        assert.Equal(t, "integration-name", deployment.Name)
@@ -182,6 +169,7 @@ func createNominalDeploymentTest() (*deploymentTrait, 
*Environment) {
                        },
                        Spec: v1.IntegrationSpec{
                                Replicas: &replicas,
+                               Traits:   make(map[string]v1.TraitSpec),
                        },
                        Status: v1.IntegrationStatus{
                                Phase: v1.IntegrationPhaseDeploying,
diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go
index e0b5cfe..34d5063 100644
--- a/pkg/trait/knative_service.go
+++ b/pkg/trait/knative_service.go
@@ -175,9 +175,6 @@ func (t *knativeServiceTrait) Apply(e *Environment) error {
        if err != nil {
                return err
        }
-       maps := e.computeConfigMaps()
-
-       e.Resources.AddAll(maps)
        e.Resources.Add(ksvc)
 
        e.Integration.Status.SetCondition(
diff --git a/pkg/trait/trait_register.go b/pkg/trait/trait_register.go
index 3f642d0..530953d 100644
--- a/pkg/trait/trait_register.go
+++ b/pkg/trait/trait_register.go
@@ -20,36 +20,38 @@ package trait
 func init() {
        // List of default trait factories.
        // Declaration order is not important, but let's keep them sorted for 
debugging.
-       AddToTraits(newInitTrait)
-       AddToTraits(newPlatformTrait)
+       AddToTraits(newAffinityTrait)
+       AddToTraits(newBuilderTrait)
        AddToTraits(newCamelTrait)
-       AddToTraits(newOpenAPITrait)
-       AddToTraits(newKnativeTrait)
-       AddToTraits(newKameletsTrait)
-       AddToTraits(newErrorHandlerTrait)
+       AddToTraits(newConfigurationTrait)
+       AddToTraits(newContainerTrait)
+       AddToTraits(newCronTrait)
        AddToTraits(newDependenciesTrait)
-       AddToTraits(newBuilderTrait)
-       AddToTraits(newQuarkusTrait)
-       AddToTraits(newEnvironmentTrait)
        AddToTraits(newDeployerTrait)
-       AddToTraits(newCronTrait)
        AddToTraits(newDeploymentTrait)
+       AddToTraits(newEnvironmentTrait)
+       AddToTraits(newErrorHandlerTrait)
        AddToTraits(newGarbageCollectorTrait)
-       AddToTraits(newAffinityTrait)
-       AddToTraits(newTolerationTrait)
-       AddToTraits(newKnativeServiceTrait)
-       AddToTraits(newServiceTrait)
-       AddToTraits(newContainerTrait)
-       AddToTraits(newPullSecretTrait)
+       AddToTraits(newIngressTrait)
+       AddToTraits(newIstioTrait)
        AddToTraits(newJolokiaTrait)
-       AddToTraits(newPrometheusTrait)
        AddToTraits(newJvmTrait)
-       AddToTraits(newRouteTrait)
-       AddToTraits(newIstioTrait)
-       AddToTraits(newIngressTrait)
-       AddToTraits(newServiceBindingTrait)
+       AddToTraits(newKameletsTrait)
+       AddToTraits(newKnativeTrait)
+       AddToTraits(newKnativeServiceTrait)
+       AddToTraits(newLoggingTraitTrait)
+       AddToTraits(newInitTrait)
+       AddToTraits(newOpenAPITrait)
        AddToTraits(newOwnerTrait)
        AddToTraits(newPdbTrait)
+       AddToTraits(newPlatformTrait)
        AddToTraits(newPodTrait)
-       AddToTraits(newLoggingTraitTrait)
+       AddToTraits(newPrometheusTrait)
+       AddToTraits(newPullSecretTrait)
+       AddToTraits(newQuarkusTrait)
+       AddToTraits(newRouteTrait)
+       AddToTraits(newServiceTrait)
+       AddToTraits(newServiceBindingTrait)
+       AddToTraits(newTolerationTrait)
+       // ^^ Declaration order is not important, but let's keep them sorted 
for debugging.
 }
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index e423830..2936ca6 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -508,7 +508,8 @@ func TestOnlySomeTraitsInfluenceBuild(t *testing.T) {
 
 func TestOnlySomeTraitsArePlatform(t *testing.T) {
        c := NewTraitTestCatalog()
-       platformTraits := []string{"builder", "camel", "jvm", "container", 
"dependencies", "deployer", "deployment", "environment", "error-handler", 
"kamelets", "openapi", "owner", "platform", "quarkus"}
+       platformTraits := []string{"builder", "camel", "jvm", "configuration", 
"container", "dependencies", "deployer",
+               "deployment", "environment", "error-handler", "kamelets", 
"openapi", "owner", "platform", "quarkus"}
 
        for _, trait := range c.AllTraits() {
                if trait.IsPlatformTrait() {

Reply via email to