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() {