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

dhanak pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/incubator-kie-kogito-serverless-operator.git


The following commit(s) were added to refs/heads/main by this push:
     new 023d0e84 kie-kogito-serverless-operator-445: Add custom configuration 
to the DI and JS for operator managed deployments (#455)
023d0e84 is described below

commit 023d0e8420df9ae871a159f1e27439becab65ee2
Author: Walter Medvedeo <[email protected]>
AuthorDate: Mon May 6 11:21:54 2024 +0200

    kie-kogito-serverless-operator-445: Add custom configuration to the DI and 
JS for operator managed deployments (#455)
    
    * kie-kogito-serverless-operator-445: Add custom configuration to the DI 
and JS for operator managed deployments
    
    * Code review suggestions 1
    
    * Code review suggestions 1
---
 controllers/platform/k8s.go                        | 22 ++++-----
 controllers/platform/services/services.go          | 34 +++++++++++---
 controllers/platform/services/services_test.go     | 53 ++++++++++++++++++++++
 .../preview/postgreSQL/02-sonataflow_platform.yaml | 14 ++++++
 utils/kubernetes/env.go                            | 12 +++++
 utils/kubernetes/env_test.go                       | 21 +++++++++
 6 files changed, 139 insertions(+), 17 deletions(-)

diff --git a/controllers/platform/k8s.go b/controllers/platform/k8s.go
index f2e5313e..e4310de8 100644
--- a/controllers/platform/k8s.go
+++ b/controllers/platform/k8s.go
@@ -106,7 +106,7 @@ func createOrUpdateDeployment(ctx context.Context, client 
client.Client, platfor
        liveProbe := readyProbe.DeepCopy()
        liveProbe.ProbeHandler.HTTPGet.Path = constants.QuarkusHealthPathLive
        imageTag := psh.GetServiceImageName(constants.PersistenceTypeEphemeral)
-       dataDeployContainer := &corev1.Container{
+       serviceContainer := &corev1.Container{
                Image:           imageTag,
                ImagePullPolicy: kubeutil.GetImagePullPolicy(imageTag),
                Env:             psh.GetEnvironmentVariables(),
@@ -127,18 +127,18 @@ func createOrUpdateDeployment(ctx context.Context, client 
client.Client, platfor
                        },
                },
        }
-       dataDeployContainer = psh.ConfigurePersistence(dataDeployContainer)
-       dataDeployContainer, err := psh.MergeContainerSpec(dataDeployContainer)
+       serviceContainer = psh.ConfigurePersistence(serviceContainer)
+       serviceContainer, err := psh.MergeContainerSpec(serviceContainer)
        if err != nil {
                return err
        }
 
        // immutable
-       dataDeployContainer.Name = psh.GetContainerName()
+       serviceContainer.Name = psh.GetContainerName()
 
        replicas := psh.GetReplicaCount()
        lbl, selectorLbl := getLabels(platform, psh)
-       dataDeploySpec := appsv1.DeploymentSpec{
+       serviceDeploymentSpec := appsv1.DeploymentSpec{
                Selector: &metav1.LabelSelector{
                        MatchLabels: selectorLbl,
                },
@@ -164,25 +164,25 @@ func createOrUpdateDeployment(ctx context.Context, client 
client.Client, platfor
                },
        }
 
-       dataDeploySpec.Template.Spec, err = 
psh.MergePodSpec(dataDeploySpec.Template.Spec)
+       serviceDeploymentSpec.Template.Spec, err = 
psh.MergePodSpec(serviceDeploymentSpec.Template.Spec)
        if err != nil {
                return err
        }
-       kubeutil.AddOrReplaceContainer(dataDeployContainer.Name, 
*dataDeployContainer, &dataDeploySpec.Template.Spec)
+       kubeutil.AddOrReplaceContainer(serviceContainer.Name, 
*serviceContainer, &serviceDeploymentSpec.Template.Spec)
 
-       dataDeploy := &appsv1.Deployment{
+       serviceDeployment := &appsv1.Deployment{
                ObjectMeta: metav1.ObjectMeta{
                        Namespace: platform.Namespace,
                        Name:      psh.GetServiceName(),
                        Labels:    lbl,
                }}
-       if err := controllerutil.SetControllerReference(platform, dataDeploy, 
client.Scheme()); err != nil {
+       if err := controllerutil.SetControllerReference(platform, 
serviceDeployment, client.Scheme()); err != nil {
                return err
        }
 
        // Create or Update the deployment
-       if op, err := controllerutil.CreateOrUpdate(ctx, client, dataDeploy, 
func() error {
-               dataDeploy.Spec = dataDeploySpec
+       if op, err := controllerutil.CreateOrUpdate(ctx, client, 
serviceDeployment, func() error {
+               serviceDeployment.Spec = serviceDeploymentSpec
 
                return nil
        }); err != nil {
diff --git a/controllers/platform/services/services.go 
b/controllers/platform/services/services.go
index 8bd6d19e..856ad90a 100644
--- a/controllers/platform/services/services.go
+++ b/controllers/platform/services/services.go
@@ -23,6 +23,7 @@ import (
        "fmt"
 
        
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/cfg"
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/utils/kubernetes"
        corev1 "k8s.io/api/core/v1"
        "k8s.io/apimachinery/pkg/api/resource"
 
@@ -238,9 +239,7 @@ func (d DataIndexHandler) 
ConfigurePersistence(containerSpec *corev1.Container)
 }
 
 func (d DataIndexHandler) MergeContainerSpec(containerSpec *corev1.Container) 
(*corev1.Container, error) {
-       c := containerSpec.DeepCopy()
-       err := mergo.Merge(c, 
d.platform.Spec.Services.DataIndex.PodTemplate.Container.ToContainer(), 
mergo.WithOverride)
-       return c, err
+       return mergeContainerSpec(containerSpec, 
&d.platform.Spec.Services.DataIndex.PodTemplate.Container)
 }
 
 func (d DataIndexHandler) GetReplicaCount() int32 {
@@ -387,9 +386,7 @@ func (j JobServiceHandler) GetReplicaCount() int32 {
 }
 
 func (j JobServiceHandler) MergeContainerSpec(containerSpec *corev1.Container) 
(*corev1.Container, error) {
-       c := containerSpec.DeepCopy()
-       err := mergo.Merge(c, 
j.platform.Spec.Services.JobService.PodTemplate.Container.ToContainer(), 
mergo.WithOverride)
-       return c, err
+       return mergeContainerSpec(containerSpec, 
&j.platform.Spec.Services.JobService.PodTemplate.Container)
 }
 
 // hasPostgreSQLConfigured returns true when either the SonataFlow Platform 
PostgreSQL CR's structure or the one in the Job service specification is not nil
@@ -482,3 +479,28 @@ func GenerateServiceURL(protocol string, namespace string, 
name string) string {
        }
        return serviceUrl
 }
+
+// mergeContainerSpec Produces the merging between the 
operatorapi.ContainerSpec provided in a SonataFlowPlatform
+// service, for example, platform.services.jobsService.podTemplate.container, 
and the destination container for the
+// corresponding service deployment. This method consider specific processing 
like not overriding environment vars
+// already configured by the operator in the destination container.
+func mergeContainerSpec(dest *corev1.Container, sourceSpec 
*operatorapi.ContainerSpec) (*corev1.Container, error) {
+       result := dest.DeepCopy()
+       source := sourceSpec.ToContainer()
+       err := mergeContainerPreservingEnvVars(result, &source)
+       return result, err
+}
+
+// mergeContainerSpecPreservingEnvVars Merges the source container into the 
dest container by giving priority to the
+// env variables already configured in the dest container when both containers 
have the same variable name.
+func mergeContainerPreservingEnvVars(dest *corev1.Container, source 
*corev1.Container) error {
+       currentEnv := dest.Env
+       if err := mergo.Merge(dest, source, mergo.WithOverride); err != nil {
+               return err
+       }
+       dest.Env = currentEnv
+       for _, envVar := range source.Env {
+               kubernetes.AddEnvIfNotPresent(dest, envVar)
+       }
+       return nil
+}
diff --git a/controllers/platform/services/services_test.go 
b/controllers/platform/services/services_test.go
new file mode 100644
index 00000000..93c186c8
--- /dev/null
+++ b/controllers/platform/services/services_test.go
@@ -0,0 +1,53 @@
+// Copyright 2024 Apache Software Foundation (ASF)
+//
+// Licensed 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 services
+
+import (
+       "testing"
+
+       operatorapi 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       "github.com/stretchr/testify/assert"
+       corev1 "k8s.io/api/core/v1"
+)
+
+func TestMergeContainerSpec(t *testing.T) {
+       container := &corev1.Container{
+               Env: []corev1.EnvVar{{Name: "var1", Value: "value1"}, {Name: 
"var2", Value: "value2"}},
+       }
+       containerSpec := &operatorapi.ContainerSpec{
+               Env: []corev1.EnvVar{{Name: "var1", Value: "value1Changed"}, 
{Name: "var3", Value: "value3"}},
+       }
+       result, err := mergeContainerSpec(container, containerSpec)
+       assert.Nil(t, err)
+       assert.Len(t, result.Env, 3)
+       assert.Equal(t, result.Env[0], corev1.EnvVar{Name: "var1", Value: 
"value1"})
+       assert.Equal(t, result.Env[1], corev1.EnvVar{Name: "var2", Value: 
"value2"})
+       assert.Equal(t, result.Env[2], corev1.EnvVar{Name: "var3", Value: 
"value3"})
+}
+
+func TestMergeContainerPreservingEnvVars(t *testing.T) {
+       container1 := &corev1.Container{
+               Env: []corev1.EnvVar{{Name: "var1", Value: "value1"}, {Name: 
"var2", Value: "value2"}},
+       }
+       container2 := &corev1.Container{
+               Env: []corev1.EnvVar{{Name: "var1", Value: "value1Changed"}, 
{Name: "var3", Value: "value3"}},
+       }
+       err := mergeContainerPreservingEnvVars(container1, container2)
+       assert.Nil(t, err)
+       assert.Len(t, container1.Env, 3)
+       assert.Equal(t, container1.Env[0], corev1.EnvVar{Name: "var1", Value: 
"value1"})
+       assert.Equal(t, container1.Env[1], corev1.EnvVar{Name: "var2", Value: 
"value2"})
+       assert.Equal(t, container1.Env[2], corev1.EnvVar{Name: "var3", Value: 
"value3"})
+}
diff --git 
a/test/testdata/platform/services/preview/postgreSQL/02-sonataflow_platform.yaml
 
b/test/testdata/platform/services/preview/postgreSQL/02-sonataflow_platform.yaml
index 9248f57b..9514b81a 100644
--- 
a/test/testdata/platform/services/preview/postgreSQL/02-sonataflow_platform.yaml
+++ 
b/test/testdata/platform/services/preview/postgreSQL/02-sonataflow_platform.yaml
@@ -37,6 +37,13 @@ spec:
             image: registry.access.redhat.com/ubi9/ubi-micro:latest
             imagePullPolicy: IfNotPresent
             command: [ 'sh', '-c', 'until (echo 1 > /dev/tcp/postgres.$(cat 
/var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local/5432)
 >/dev/null 2>&1; do echo "Waiting for postgres server"; sleep 3; done;' ]
+        container:
+          env:
+            - name: MY_CUSTOM_VARIABLE
+              value: "OKAY"
+            - name: QUARKUS_DATASOURCE_PASSWORD
+#             This value should not be used since it's already set by the 
operator. If used, the test will fail.
+              value: "SHOULD_NOT_BE_USED"
     jobService:
       enabled: true
       persistence:
@@ -52,3 +59,10 @@ spec:
             image: registry.access.redhat.com/ubi9/ubi-micro:latest
             imagePullPolicy: IfNotPresent
             command: [ 'sh', '-c', 'until (echo 1 > /dev/tcp/postgres.$(cat 
/var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local/5432)
 >/dev/null 2>&1; do echo "Waiting for postgres server"; sleep 3; done;' ]
+        container:
+          env:
+            - name: MY_CUSTOM_VARIABLE
+              value: "OKAY"
+            - name: QUARKUS_DATASOURCE_PASSWORD
+#             This value should not be used since it's already set by the 
operator. If used, the test will fail.
+              value: "SHOULD_NOT_BE_USED"
\ No newline at end of file
diff --git a/utils/kubernetes/env.go b/utils/kubernetes/env.go
index 5ca80656..df9f9432 100644
--- a/utils/kubernetes/env.go
+++ b/utils/kubernetes/env.go
@@ -36,3 +36,15 @@ func CreateOrReplaceEnv(container *v1.Container, name, value 
string) {
                })
        }
 }
+
+// AddEnvIfNotPresent Adds and env variable to a container if not already 
present. Returns true if the variable didn't exist
+// and was added, false in any other case.
+func AddEnvIfNotPresent(container *v1.Container, envVar v1.EnvVar) bool {
+       for i := range container.Env {
+               if container.Env[i].Name == envVar.Name {
+                       return false
+               }
+       }
+       container.Env = append(container.Env, envVar)
+       return true
+}
diff --git a/utils/kubernetes/env_test.go b/utils/kubernetes/env_test.go
index d209edd6..a37c948c 100644
--- a/utils/kubernetes/env_test.go
+++ b/utils/kubernetes/env_test.go
@@ -56,3 +56,24 @@ func TestCreateOrReplaceEnv(t *testing.T) {
        CreateOrReplaceEnv(&containerWithEnv.Spec.Template.Spec.Containers[0], 
"myvar", "mutated")
        assert.Equal(t, "mutated", 
containerWithEnv.Spec.Template.Spec.Containers[0].Env[0].Value)
 }
+
+func TestAddIfNotPresent(t *testing.T) {
+       containerNoEnv := &v1.Container{Env: nil}
+
+       wasAdded := AddEnvIfNotPresent(containerNoEnv, v1.EnvVar{Name: "var1", 
Value: "value1"})
+       assert.True(t, wasAdded)
+       assert.Equal(t, v1.EnvVar{Name: "var1", Value: "value1"}, 
containerNoEnv.Env[0])
+
+       containerWithEnv := &v1.Container{Env: []v1.EnvVar{{Name: "var1", 
Value: "value1"}, {Name: "var2", Value: "value2"}}}
+       wasAdded = AddEnvIfNotPresent(containerWithEnv, v1.EnvVar{Name: "var1", 
Value: "value1Changed"})
+       assert.False(t, wasAdded)
+       assert.Equal(t, v1.EnvVar{Name: "var1", Value: "value1"}, 
containerWithEnv.Env[0])
+       assert.Equal(t, v1.EnvVar{Name: "var2", Value: "value2"}, 
containerWithEnv.Env[1])
+
+       wasAdded = AddEnvIfNotPresent(containerWithEnv, v1.EnvVar{Name: "var3", 
Value: "value3"})
+       assert.True(t, wasAdded)
+
+       assert.Equal(t, v1.EnvVar{Name: "var1", Value: "value1"}, 
containerWithEnv.Env[0])
+       assert.Equal(t, v1.EnvVar{Name: "var2", Value: "value2"}, 
containerWithEnv.Env[1])
+       assert.Equal(t, v1.EnvVar{Name: "var3", Value: "value3"}, 
containerWithEnv.Env[2])
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to