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 eeab78ce0064a0a60b394f4955a802ded3d3c811
Author: mmelko <[email protected]>
AuthorDate: Fri Dec 11 15:22:21 2020 +0100

    Add podTrait to specify customPodTemplateSpec
    close #1657
---
 pkg/cmd/run.go              |  10 ++-
 pkg/trait/pod.go            | 111 +++++++++++++++++++++++++++++++
 pkg/trait/pod_test.go       | 156 ++++++++++++++++++++++++++++++++++++++++++++
 pkg/trait/trait_register.go |   1 +
 4 files changed, 277 insertions(+), 1 deletion(-)

diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 255feaf..1cd8fb8 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -746,7 +746,15 @@ func configureTraits(options []string, catalog 
*trait.Catalog) (map[string]v1.Tr
                        // $ kamel run --trait 
<trait>.<property>=<value_1>,...,<trait>.<property>=<value_N>
                        traits[id][prop] = []string{v, value}
                case nil:
-                       traits[id][prop] = value
+                       //check if value is a path to the file
+                       if _, err := os.Stat(value); err == nil {
+                               rsc, errResolve := 
ResolveSources(context.TODO(), []string{value}, false)
+                               if errResolve == nil && len(rsc) > 0 {
+                                       traits[id][prop] = rsc[0].Content
+                               }
+                       } else {
+                               traits[id][prop] = value
+                       }
                }
        }
 
diff --git a/pkg/trait/pod.go b/pkg/trait/pod.go
new file mode 100644
index 0000000..051f68d
--- /dev/null
+++ b/pkg/trait/pod.go
@@ -0,0 +1,111 @@
+/*
+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/ghodss/yaml"
+       v12 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/util/json"
+       
"k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch"
+
+       //"github.com/ghodss/yaml"
+       //      cmd "github.com/apache/camel-k/pkg/cmd"
+
+       appsv1 "k8s.io/api/apps/v1"
+)
+
+// The Pod trait allows to modify the custom PodTemplateSpec resource for the 
Integration pods
+//
+// +camel-k:trait=pdb
+type podTrait struct {
+       BaseTrait `property:",squash"`
+       Template  string `property:"template"`
+}
+
+func newPodTrait() Trait {
+       return &podTrait{
+               BaseTrait: NewBaseTrait("pod", 1800),
+       }
+}
+
+func (t *podTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled == nil || !*t.Enabled {
+               return false, nil
+       }
+
+       if t.Template == "" {
+               return false, fmt.Errorf("template must be specified")
+       }
+
+       if _, err := t.parseTemplate(); err != nil {
+               return false, err
+       }
+
+       return e.IntegrationInPhase(
+               v1.IntegrationPhaseDeploying,
+               v1.IntegrationPhaseRunning,
+       ), nil
+}
+
+func (t *podTrait) Apply(e *Environment) error {
+       var deployment *appsv1.Deployment
+
+       e.Resources.VisitDeployment(func(d *appsv1.Deployment) {
+               if d.Name == e.Integration.Name {
+                       deployment = d
+               }
+       })
+
+       modifiedTemplate, err := 
t.mergeIntoTemplateSpec(deployment.Spec.Template, []byte(t.Template))
+       if err != nil {
+               return err
+       }
+       deployment.Spec.Template = *modifiedTemplate
+       return nil
+}
+
+func (t *podTrait) parseTemplate() (*v12.PodTemplateSpec, error) {
+       var template *v12.PodTemplateSpec
+
+       if err := yaml.Unmarshal([]byte(t.Template), &template); err != nil {
+               return nil, err
+       }
+       return template, nil
+}
+
+func (t *podTrait) mergeIntoTemplateSpec(template v12.PodTemplateSpec, 
changesBytes []byte) (mergedTemplate *v12.PodTemplateSpec, err error) {
+       patch, err := yaml.YAMLToJSON(changesBytes)
+       if err != nil {
+               return nil, err
+       }
+
+       sourceJson, err := json.Marshal(template)
+       if err != nil {
+               return nil, err
+       }
+
+       patched, err := strategicpatch.StrategicMergePatch(sourceJson, patch, 
v12.PodTemplateSpec{})
+       if err != nil {
+               return nil, err
+       }
+
+       err = json.Unmarshal(patched, &mergedTemplate)
+       return
+}
diff --git a/pkg/trait/pod_test.go b/pkg/trait/pod_test.go
new file mode 100755
index 0000000..61cc77e
--- /dev/null
+++ b/pkg/trait/pod_test.go
@@ -0,0 +1,156 @@
+package trait
+
+import (
+       v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+
+       "github.com/ghodss/yaml"
+       "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"
+       "testing"
+)
+
+func TestConfigurePodTraitDoesSucceed(t *testing.T) {
+       trait, environment, _ := createPodTest()
+       configured, err := trait.Configure(environment)
+
+       assert.False(t, configured)
+       assert.NotNil(t, err)
+
+       trait.Template = "{}"
+       configured, err = trait.Configure(environment)
+
+       assert.True(t, configured)
+       assert.Nil(t, err)
+}
+
+func TestSimpleChange(t *testing.T) {
+       templateString := "metadata:\n  name: template-test\n\nspec:\n  
containers:\n    - name: second-container"
+       template := testPodTemplateSpec(t, templateString)
+
+       assert.Equal(t, "template-test", template.Name)
+       assert.Equal(t, 3, len(template.Spec.Containers))
+}
+
+func TestMergeArrays(t *testing.T) {
+       templateString := "{metadata: {name: test-template}, " +
+               "spec: {containers: [{name: second-container, " +
+               "env: [{name: SOME_VARIABLE, value: SOME_VALUE}, {name: 
SOME_VARIABLE2, value: SOME_VALUE2}]}, " +
+               "{name: integration, env: [{name: TEST_ADDED_CUSTOM_VARIABLE, 
value: value}]}" +
+               "]" +
+               "}}"
+       templateSpec := testPodTemplateSpec(t, templateString)
+
+       assert.Equal(t, "test-template", templateSpec.Name)
+       assert.NotNil(t, getContainer(templateSpec.Spec.Containers, 
"second-container"))
+       assert.Equal(t, "SOME_VALUE", containsEnvVariables(templateSpec, 
"second-container", "SOME_VARIABLE"))
+       assert.Equal(t, "SOME_VALUE2", containsEnvVariables(templateSpec, 
"second-container", "SOME_VARIABLE2"))
+       assert.True(t, len(getContainer(templateSpec.Spec.Containers, 
"integration").Env) > 1)
+       assert.Equal(t, "value", containsEnvVariables(templateSpec, 
"integration", "TEST_ADDED_CUSTOM_VARIABLE"))
+}
+
+func TestChangeEnvVariables(t *testing.T) {
+       templateString := "{metadata: {name: test-template}, " +
+               "spec: {containers: [" +
+               "{name: second, env: [{name: TEST_VARIABLE, value: 
TEST_VALUE}]}, " +
+               "{name: integration, env: [{name: CAMEL_K_DIGEST, value: 
new_value}]}" +
+               "]}}"
+       templateSpec := testPodTemplateSpec(t, templateString)
+
+       //check if env var was added in second container
+       assert.Equal(t, containsEnvVariables(templateSpec, "second", 
"TEST_VARIABLE"), "TEST_VALUE")
+       assert.Equal(t, 3, len(getContainer(templateSpec.Spec.Containers, 
"second").Env))
+
+       //check if env var was changed
+       assert.Equal(t, containsEnvVariables(templateSpec, "integration", 
"CAMEL_K_DIGEST"), "new_value")
+}
+func TestRemoveArray(t *testing.T) {
+       templateString := "{metadata: " +
+               "{name: test-template}, " +
+               "spec:" +
+               " {containers: " +
+               "[{name: second-container, env: [{name: SOME_VARIABLE, value: 
SOME_VALUE}, {name: SOME_VARIABLE2, value: SOME_VALUE2}]}, " +
+               "{name: integration, env: null}" +
+               "]}}"
+
+       templateSpec := testPodTemplateSpec(t, templateString)
+       assert.True(t, len(getContainer(templateSpec.Spec.Containers, 
"integration").Env) == 0)
+}
+
+func createPodTest() (*podTrait, *Environment, *appsv1.Deployment) {
+       trait := newPodTrait().(*podTrait)
+       enabled := true
+       trait.Enabled = &enabled
+
+       specTemplateYamlString := "{metadata: {name: example-template, 
creationTimestamp: null, " +
+               "labels: {camel.apache.org/integration: test}}, " +
+               "spec: {volumes: [" +
+               "{name: i-source-000," + "configMap: {name: test-source-000, 
items: [{key: content, path: test.groovy}], defaultMode: 420}}, " +
+               "{name: application-properties, configMap: {name: 
test-application-properties, items: [{key: application.properties, path: 
application.properties}], defaultMode: 420}}], " +
+               "containers: [" +
+               "{name: second, env: [{name: SOME_VARIABLE, value: SOME_VALUE}, 
{name: SOME_VARIABLE2, value: SOME_VALUE2}]}," +
+               "{name: integration, command: [/bin/sh, '-c'], env: [{name: 
CAMEL_K_DIGEST, value: vO3wwJHC7-uGEiFFVac0jq6rZT5EZNw56Ae5gKKFZZsk}, {name: 
CAMEL_K_CONF, value: /etc/camel/conf/application.properties}, {name: 
CAMEL_K_CONF_D, value: /etc/camel/conf.d},{name: CAMEL_K_VERSION, value: 
1.3.0-SNAPSHOT}, {name: CAMEL_K_INTEGRATION, value: test}, {name: 
CAMEL_K_RUNTIME_VERSION, value: 1.5.0}, {name: CAMEL_K_MOUNT_PATH_CONFIGMAPS, 
value: /etc/camel/conf.d/_configmaps}, {name: CAMEL_K_MOUNT_PATH_SE [...]
+       var template corev1.PodTemplateSpec
+       _ = yaml.Unmarshal([]byte(specTemplateYamlString), &template)
+
+       deployment := &appsv1.Deployment{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name: "pod-template-test-integration",
+               },
+               Spec: appsv1.DeploymentSpec{
+                       Template: template,
+               },
+       }
+       environment := &Environment{
+               Integration: &v1.Integration{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name: "pod-template-test-integration",
+                       },
+                       Status: v1.IntegrationStatus{
+                               Phase: v1.IntegrationPhaseDeploying,
+                       },
+               },
+               Resources: kubernetes.NewCollection(deployment),
+       }
+       return trait, environment, deployment
+}
+
+func containsEnvVariables(template corev1.PodTemplateSpec, containerName 
string, name string) string {
+       container := getContainer(template.Spec.Containers, containerName)
+
+       for i := range container.Env {
+               envv := container.Env[i]
+               if envv.Name == name {
+                       return envv.Value
+               }
+       }
+       return "not found!"
+}
+
+func getContainer(containers []corev1.Container, name string) 
*corev1.Container {
+       for i := range containers {
+               if containers[i].Name == name {
+                       return &containers[i]
+               }
+       }
+       return nil
+}
+
+func testPodTemplateSpec(t *testing.T, template string) corev1.PodTemplateSpec 
{
+       trait, environment, _ := createPodTest()
+       trait.Template = template
+
+       _, err := trait.Configure(environment)
+       assert.Nil(t, err)
+
+       err = trait.Apply(environment)
+       assert.Nil(t, err)
+
+       deployment := environment.Resources.GetDeployment(func(deployment 
*appsv1.Deployment) bool {
+               return deployment.Name == "pod-template-test-integration"
+       })
+
+       return deployment.Spec.Template
+}
diff --git a/pkg/trait/trait_register.go b/pkg/trait/trait_register.go
index f8e0794..3f642d0 100644
--- a/pkg/trait/trait_register.go
+++ b/pkg/trait/trait_register.go
@@ -50,5 +50,6 @@ func init() {
        AddToTraits(newServiceBindingTrait)
        AddToTraits(newOwnerTrait)
        AddToTraits(newPdbTrait)
+       AddToTraits(newPodTrait)
        AddToTraits(newLoggingTraitTrait)
 }

Reply via email to