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]