astefanutti commented on a change in pull request #1861:
URL: https://github.com/apache/camel-k/pull/1861#discussion_r621951297



##########
File path: go.mod
##########
@@ -13,6 +13,7 @@ require (
        github.com/evanphx/json-patch v4.9.0+incompatible
        github.com/fatih/structs v1.1.0
        github.com/gertd/go-pluralize v0.1.1
+       github.com/ghodss/yaml v1.0.0

Review comment:
       This can be removed now.

##########
File path: pkg/cmd/run.go
##########
@@ -35,10 +35,12 @@ import (
        "github.com/pkg/errors"
        "github.com/spf13/cobra"
        "github.com/spf13/pflag"
+       //"gopkg.in/yaml.v3"

Review comment:
       This can be removed.

##########
File path: pkg/trait/pod.go
##########
@@ -0,0 +1,121 @@
+/*
+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"
+       "k8s.io/api/batch/v1beta1"
+       corev1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/util/json"
+       "k8s.io/apimachinery/pkg/util/strategicpatch"
+       serving "knative.dev/serving/pkg/apis/serving/v1"
+
+       appsv1 "k8s.io/api/apps/v1"
+)
+
+//    The pod trait allows the customization of the Integration pods.
+//    It applies the `PodSpecTemplate` struct contained in the Integration 
`.spec.podTemplate` field, into the Integration deployment Pods template, using 
strategic merge patch.
+//
+//    This can be used to customize the container where Camel routes execute, 
by using the `integration` container name.
+//
+// +camel-k:trait=pod
+type podTrait struct {
+       BaseTrait `property:",squash"`
+}
+
+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 e.Integration != nil && e.Integration.Spec.PodTemplate == nil {
+               return false, nil
+       }
+
+       return e.IntegrationInPhase(
+               v1.IntegrationPhaseDeploying,
+               v1.IntegrationPhaseRunning,
+       ), nil
+}
+
+func (t *podTrait) Apply(e *Environment) error {
+       changes := e.Integration.Spec.PodTemplate.Spec
+       var patchedPodSpec *corev1.PodSpec
+       strategy, err := e.DetermineControllerStrategy()
+       if err != nil {
+               return fmt.Errorf("unable to determine the controller stratedy")
+       }
+
+       if strategy == ControllerStrategyCronJob {
+               e.Resources.VisitCronJob(func(c *v1beta1.CronJob) {
+                       if c.Name == e.Integration.Name {
+                               if patchedPodSpec, err = 
t.applyChangesTo(&c.Spec.JobTemplate.Spec.Template.Spec, changes); err == nil {
+                                       c.Spec.JobTemplate.Spec.Template.Spec = 
*patchedPodSpec
+                               }
+                       }
+               })
+       } else if strategy == ControllerStrategyDeployment {
+               e.Resources.VisitDeployment(func(d *appsv1.Deployment) {
+                       if d.Name == e.Integration.Name {
+                               if patchedPodSpec, err = 
t.applyChangesTo(&d.Spec.Template.Spec, changes); err == nil {
+                                       d.Spec.Template.Spec = *patchedPodSpec
+                               }
+                       }
+               })
+       } else {
+               e.Resources.VisitKnativeService(func(s *serving.Service) {
+                       if s.Name == e.Integration.Name {
+                               if patchedPodSpec, err = 
t.applyChangesTo(&s.Spec.Template.Spec.PodSpec, changes); err == nil {
+                                       s.Spec.Template.Spec.PodSpec = * 
patchedPodSpec
+                               }
+                       }
+               })
+       }
+
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (t *podTrait) applyChangesTo(podSpec *corev1.PodSpec, changes v1.PodSpec) 
(patchedPodSpec *corev1.PodSpec, err error) {
+       patch, err := json.Marshal(changes)
+       if err != nil {
+               return nil, err
+       }
+
+       sourceJson, err := json.Marshal(podSpec)
+       if err != nil {
+               return
+       }
+
+       patched, err := strategicpatch.StrategicMergePatch(sourceJson, patch, 
corev1.PodSpec{})
+       if err != nil {
+               return
+       }
+
+       err = json.Unmarshal(patched, &patchedPodSpec)
+       return

Review comment:
       This can be changed to `return json.Unmarshal(patched, &patchedPodSpec)`.

##########
File path: pkg/trait/pod.go
##########
@@ -0,0 +1,121 @@
+/*
+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"
+       "k8s.io/api/batch/v1beta1"
+       corev1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/util/json"
+       "k8s.io/apimachinery/pkg/util/strategicpatch"
+       serving "knative.dev/serving/pkg/apis/serving/v1"
+
+       appsv1 "k8s.io/api/apps/v1"
+)
+
+//    The pod trait allows the customization of the Integration pods.
+//    It applies the `PodSpecTemplate` struct contained in the Integration 
`.spec.podTemplate` field, into the Integration deployment Pods template, using 
strategic merge patch.
+//
+//    This can be used to customize the container where Camel routes execute, 
by using the `integration` container name.
+//
+// +camel-k:trait=pod
+type podTrait struct {
+       BaseTrait `property:",squash"`
+}
+
+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 e.Integration != nil && e.Integration.Spec.PodTemplate == nil {
+               return false, nil
+       }
+
+       return e.IntegrationInPhase(
+               v1.IntegrationPhaseDeploying,
+               v1.IntegrationPhaseRunning,
+       ), nil
+}
+
+func (t *podTrait) Apply(e *Environment) error {
+       changes := e.Integration.Spec.PodTemplate.Spec
+       var patchedPodSpec *corev1.PodSpec
+       strategy, err := e.DetermineControllerStrategy()
+       if err != nil {
+               return fmt.Errorf("unable to determine the controller stratedy")
+       }
+
+       if strategy == ControllerStrategyCronJob {
+               e.Resources.VisitCronJob(func(c *v1beta1.CronJob) {
+                       if c.Name == e.Integration.Name {
+                               if patchedPodSpec, err = 
t.applyChangesTo(&c.Spec.JobTemplate.Spec.Template.Spec, changes); err == nil {
+                                       c.Spec.JobTemplate.Spec.Template.Spec = 
*patchedPodSpec
+                               }
+                       }
+               })
+       } else if strategy == ControllerStrategyDeployment {
+               e.Resources.VisitDeployment(func(d *appsv1.Deployment) {
+                       if d.Name == e.Integration.Name {
+                               if patchedPodSpec, err = 
t.applyChangesTo(&d.Spec.Template.Spec, changes); err == nil {
+                                       d.Spec.Template.Spec = *patchedPodSpec
+                               }
+                       }
+               })
+       } else {
+               e.Resources.VisitKnativeService(func(s *serving.Service) {
+                       if s.Name == e.Integration.Name {
+                               if patchedPodSpec, err = 
t.applyChangesTo(&s.Spec.Template.Spec.PodSpec, changes); err == nil {
+                                       s.Spec.Template.Spec.PodSpec = * 
patchedPodSpec
+                               }
+                       }
+               })
+       }
+
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (t *podTrait) applyChangesTo(podSpec *corev1.PodSpec, changes v1.PodSpec) 
(patchedPodSpec *corev1.PodSpec, err error) {

Review comment:
       Is `patchedPodSpec *corev1.PodSpec` needed in the return signature?

##########
File path: pkg/trait/pod.go
##########
@@ -0,0 +1,121 @@
+/*
+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"
+       "k8s.io/api/batch/v1beta1"
+       corev1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/util/json"
+       "k8s.io/apimachinery/pkg/util/strategicpatch"
+       serving "knative.dev/serving/pkg/apis/serving/v1"
+
+       appsv1 "k8s.io/api/apps/v1"
+)
+
+//    The pod trait allows the customization of the Integration pods.
+//    It applies the `PodSpecTemplate` struct contained in the Integration 
`.spec.podTemplate` field, into the Integration deployment Pods template, using 
strategic merge patch.
+//
+//    This can be used to customize the container where Camel routes execute, 
by using the `integration` container name.
+//
+// +camel-k:trait=pod
+type podTrait struct {
+       BaseTrait `property:",squash"`
+}
+
+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 e.Integration != nil && e.Integration.Spec.PodTemplate == nil {
+               return false, nil
+       }
+
+       return e.IntegrationInPhase(
+               v1.IntegrationPhaseDeploying,
+               v1.IntegrationPhaseRunning,
+       ), nil
+}
+
+func (t *podTrait) Apply(e *Environment) error {
+       changes := e.Integration.Spec.PodTemplate.Spec
+       var patchedPodSpec *corev1.PodSpec
+       strategy, err := e.DetermineControllerStrategy()
+       if err != nil {
+               return fmt.Errorf("unable to determine the controller stratedy")
+       }
+
+       if strategy == ControllerStrategyCronJob {

Review comment:
       Maybe a switch statement would be more appropriate?

##########
File path: docs/modules/traits/pages/pod.adoc
##########
@@ -0,0 +1,33 @@
+= Pod Trait
+
+// Start of autogenerated code - DO NOT EDIT! (description)
+   The pod trait allows the customization of the Integration pods.
+   It applies the `PodSpecTemplate` struct contained in the Integration 
`.spec.podTemplate` field, into the Integration deployment Pods template, using 
strategic merge patch.
+
+   This can be used to customize the container where Camel routes execute, by 
using the `integration` container name.
+
+
+This trait is available in the following profiles: **Kubernetes, Knative, 
OpenShift**.
+
+// End of autogenerated code - DO NOT EDIT! (description)
+// Start of autogenerated code - DO NOT EDIT! (configuration)

Review comment:
       Could you add the example from the e2e test?

##########
File path: pkg/trait/pod_test.go
##########
@@ -0,0 +1,157 @@
+package trait
+
+import (
+       v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       "github.com/stretchr/testify/assert"
+       "gopkg.in/yaml.v2"
+       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.True(t, configured)
+       assert.Nil(t, err)
+
+       configured, err = trait.Configure(environment)
+
+       assert.True(t, configured)
+       assert.Nil(t, err)
+}
+
+//
+func TestSimpleChange(t *testing.T) {
+       templateString := `containers:
+  - name: second-container
+    env:
+      - name: test
+        value: test`
+       template := testPodTemplateSpec(t, templateString)
+
+       assert.Equal(t, 3, len(template.Spec.Containers))
+}
+
+func TestMergeArrays(t *testing.T) {
+       templateString :=
+               "{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 := "{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 createPodTest(templateString string) (*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_SECRETS, 
value: /etc/camel/conf.d/_secrets}, {name: NAMESPACE, valueFrom: {fieldRef: 
{apiVersion: v1, fieldPath: metadata.namespace}}}, {name: POD_NAME, valueFrom: 
{fieldRef: {apiVersion: v1, fieldPath: metadata.name}}}], imagePullPolicy: 
IfNotPresent, volumeMounts: [{name: i-source-000, mountPath: 
/etc/camel/sources/i-source-000}, {name: application-properties, mountPath: 
/etc/camel/conf}], terminationMessagePolicy: File, image: 
'image-registry.openshift-image-registry.svc:5000/podtrait/camel-k-kit-bv
 
d7utv170hult6ju26g@sha256:1c091437ef986f2852733da5f3fce7a5f48a5ea51e409f0bdcb0c13ff620e6b2',
 workingDir: /deployments, args: ['echo exec java -cp 
./resources:/etc/camel/conf:/etc/camel/resources:/etc/camel/sources/i-source-000
 io.quarkus.runner.GeneratedMain']}], restartPolicy: Always, 
terminationGracePeriodSeconds: 90, dnsPolicy: ClusterFirst, securityContext: 
{}, schedulerName: default-scheduler}}"
+       var template corev1.PodTemplateSpec
+       _ = yaml.Unmarshal([]byte(specTemplateYamlString), &template)
+
+       var podTemplateIt v1.PodSpec
+       if templateString != "" {
+               _ = yaml.Unmarshal([]byte(templateString), &podTemplateIt)
+       }
+
+       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,
+                       },
+                       Spec: v1.IntegrationSpec{
+                               PodTemplate: &v1.PodSpecTemplate{
+                                       Spec: podTemplateIt,
+                               },
+                       },
+               },
+
+               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(template)
+       //trait.Template = template

Review comment:
       This can be removed.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


Reply via email to