Skip to content

Commit

Permalink
[KOGITO-9982] Add Persistence field in SonataFlow CRD (#322)
Browse files Browse the repository at this point in the history
* Added Persistence field in SonataFlow CRD

Signed-off-by: Jordi Gil <[email protected]>

* Added implementation to manage PostgreSQL persistence type in the SonataFlow CRD

Signed-off-by: Jordi Gil <[email protected]>

* Added end to end tests

Signed-off-by: Jordi Gil <[email protected]>

* Retry health endpoints

Signed-off-by: Jordi Gil <[email protected]>

* Changes based on Ricardo's feedback

Signed-off-by: Jordi Gil <[email protected]>

* Reorganized persistence e2e test and tweaked health check to cover for worfklows that end very quickly, like callbackstatetimeouts

Signed-off-by: Jordi Gil <[email protected]>

* Changes after merge conflicts with platform e2e tests

Signed-off-by: Jordi Gil <[email protected]>

* Added additional check for failing test in workflowproj

Signed-off-by: Jordi Gil <[email protected]>

---------

Signed-off-by: Jordi Gil <[email protected]>
  • Loading branch information
jordigilh committed Jan 26, 2024
1 parent cccb7b2 commit 2b5056d
Show file tree
Hide file tree
Showing 18 changed files with 855 additions and 151 deletions.
3 changes: 3 additions & 0 deletions api/v1alpha08/sonataflow_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,9 @@ type SonataFlowSpec struct {
// PodTemplate describes the deployment details of this SonataFlow instance.
//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="podTemplate"
PodTemplate PodTemplateSpec `json:"podTemplate,omitempty"`
// Persists service to a datasource of choice. Ephemeral by default.
// +optional
Persistence *PersistenceOptions `json:"persistence,omitempty"`
}

// SonataFlowStatus defines the observed state of SonataFlow
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha08/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 59 additions & 0 deletions bundle/manifests/sonataflow.org_sonataflows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,65 @@ spec:
required:
- states
type: object
persistence:
description: Persists service to a datasource of choice. Ephemeral
by default.
maxProperties: 1
properties:
postgresql:
description: Connect configured services to a postgresql database.
maxProperties: 2
minProperties: 2
properties:
jdbcUrl:
description: PostgreSql JDBC URL. Mutually exclusive to serviceRef.
e.g. "jdbc:postgresql://host:port/database?currentSchema=data-index-service"
type: string
secretRef:
description: Secret reference to the database user credentials
properties:
name:
description: Name of the postgresql credentials secret.
type: string
passwordKey:
description: Defaults to POSTGRESQL_PASSWORD
type: string
userKey:
description: Defaults to POSTGRESQL_USER
type: string
required:
- name
type: object
serviceRef:
description: Service reference to postgresql datasource. Mutually
exclusive to jdbcUrl.
properties:
databaseName:
description: Name of postgresql database to be used. Defaults
to "sonataflow"
type: string
databaseSchema:
description: Schema of postgresql database to be used.
Defaults to "data-index-service"
type: string
name:
description: Name of the postgresql k8s service.
type: string
namespace:
description: Namespace of the postgresql k8s service.
Defaults to the SonataFlowPlatform's local namespace.
type: string
port:
description: Port to use when connecting to the postgresql
k8s service. Defaults to 5432.
type: integer
required:
- name
type: object
required:
- secretRef
type: object
type: object
podTemplate:
description: PodTemplate describes the deployment details of this
SonataFlow instance.
Expand Down
59 changes: 59 additions & 0 deletions config/crd/bases/sonataflow.org_sonataflows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2103,6 +2103,65 @@ spec:
required:
- states
type: object
persistence:
description: Persists service to a datasource of choice. Ephemeral
by default.
maxProperties: 1
properties:
postgresql:
description: Connect configured services to a postgresql database.
maxProperties: 2
minProperties: 2
properties:
jdbcUrl:
description: PostgreSql JDBC URL. Mutually exclusive to serviceRef.
e.g. "jdbc:postgresql://host:port/database?currentSchema=data-index-service"
type: string
secretRef:
description: Secret reference to the database user credentials
properties:
name:
description: Name of the postgresql credentials secret.
type: string
passwordKey:
description: Defaults to POSTGRESQL_PASSWORD
type: string
userKey:
description: Defaults to POSTGRESQL_USER
type: string
required:
- name
type: object
serviceRef:
description: Service reference to postgresql datasource. Mutually
exclusive to jdbcUrl.
properties:
databaseName:
description: Name of postgresql database to be used. Defaults
to "sonataflow"
type: string
databaseSchema:
description: Schema of postgresql database to be used.
Defaults to "data-index-service"
type: string
name:
description: Name of the postgresql k8s service.
type: string
namespace:
description: Namespace of the postgresql k8s service.
Defaults to the SonataFlowPlatform's local namespace.
type: string
port:
description: Port to use when connecting to the postgresql
k8s service. Defaults to 5432.
type: integer
required:
- name
type: object
required:
- secretRef
type: object
type: object
podTemplate:
description: PodTemplate describes the deployment details of this
SonataFlow instance.
Expand Down
148 changes: 12 additions & 136 deletions controllers/platform/services/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,24 @@ package services

import (
"fmt"
"strconv"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"

operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants"
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/persistence"
"github.com/magiconair/properties"

"github.com/apache/incubator-kie-kogito-serverless-operator/version"
"github.com/imdario/mergo"
)

const (
quarkusHibernateORMDatabaseGeneration string = "QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION"
quarkusFlywayMigrateAtStart string = "QUARKUS_FLYWAY_MIGRATE_AT_START"
)

type PlatformServiceHandler interface {
// GetContainerName returns the name of the service's container in the deployment.
GetContainerName() string
Expand Down Expand Up @@ -137,7 +142,9 @@ func (d DataIndexHandler) ConfigurePersistence(containerSpec *corev1.Container)
if d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSql != nil {
c := containerSpec.DeepCopy()
c.Image = d.GetServiceImageName(constants.PersistenceTypePostgreSQL)
c.Env = append(c.Env, d.configurePostgreSqlEnv(d.platform.Spec.Services.DataIndex.Persistence.PostgreSql, d.GetServiceName(), d.platform.Namespace)...)
c.Env = append(c.Env, persistence.ConfigurePostgreSqlEnv(d.platform.Spec.Services.DataIndex.Persistence.PostgreSql, d.GetServiceName(), d.platform.Namespace)...)
// specific to DataIndex
c.Env = append(c.Env, corev1.EnvVar{Name: quarkusHibernateORMDatabaseGeneration, Value: "update"}, corev1.EnvVar{Name: quarkusFlywayMigrateAtStart, Value: "true"})
return c
}
return containerSpec
Expand All @@ -160,74 +167,6 @@ func (d DataIndexHandler) GetServiceCmName() string {
return fmt.Sprintf("%s-props", d.GetServiceName())
}

func (d DataIndexHandler) configurePostgreSqlEnv(postgresql *operatorapi.PersistencePostgreSql, databaseSchema, databaseNamespace string) []corev1.EnvVar {
dataSourcePort := constants.DefaultPostgreSQLPort
databaseName := "sonataflow"
dataSourceURL := postgresql.JdbcUrl
if postgresql.ServiceRef != nil {
if len(postgresql.ServiceRef.DatabaseSchema) > 0 {
databaseSchema = postgresql.ServiceRef.DatabaseSchema
}
if len(postgresql.ServiceRef.Namespace) > 0 {
databaseNamespace = postgresql.ServiceRef.Namespace
}
if postgresql.ServiceRef.Port != nil {
dataSourcePort = *postgresql.ServiceRef.Port
}
if len(postgresql.ServiceRef.DatabaseName) > 0 {
databaseName = postgresql.ServiceRef.DatabaseName
}
dataSourceURL = "jdbc:" + constants.PersistenceTypePostgreSQL + "://" + postgresql.ServiceRef.Name + "." + databaseNamespace + ":" + strconv.Itoa(dataSourcePort) + "/" + databaseName + "?currentSchema=" + databaseSchema
}
secretRef := corev1.LocalObjectReference{
Name: postgresql.SecretRef.Name,
}
quarkusDatasourceUsername := "POSTGRESQL_USER"
if len(postgresql.SecretRef.UserKey) > 0 {
quarkusDatasourceUsername = postgresql.SecretRef.UserKey
}
quarkusDatasourcePassword := "POSTGRESQL_PASSWORD"
if len(postgresql.SecretRef.PasswordKey) > 0 {
quarkusDatasourcePassword = postgresql.SecretRef.PasswordKey
}
return []corev1.EnvVar{
{
Name: "QUARKUS_DATASOURCE_USERNAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: quarkusDatasourceUsername,
LocalObjectReference: secretRef,
},
},
},
{
Name: "QUARKUS_DATASOURCE_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: quarkusDatasourcePassword,
LocalObjectReference: secretRef,
},
},
},
{
Name: "QUARKUS_DATASOURCE_DB_KIND",
Value: constants.PersistenceTypePostgreSQL,
},
{
Name: "QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION",
Value: "update",
},
{
Name: "QUARKUS_FLYWAY_MIGRATE_AT_START",
Value: "true",
},
{
Name: "QUARKUS_DATASOURCE_JDBC_URL",
Value: dataSourceURL,
},
}
}

func (d DataIndexHandler) GenerateWorkflowProperties() (*properties.Properties, error) {
props := properties.NewProperties()
if d.platform.Spec.Services.DataIndex != nil {
Expand Down Expand Up @@ -318,7 +257,9 @@ func (j JobServiceHandler) ConfigurePersistence(containerSpec *corev1.Container)
if j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSql != nil {
c := containerSpec.DeepCopy()
c.Image = j.GetServiceImageName(constants.PersistenceTypePostgreSQL)
c.Env = append(c.Env, j.configurePostgreSqlEnv(j.platform.Spec.Services.JobService.Persistence.PostgreSql, j.GetServiceName(), j.platform.Namespace)...)
c.Env = append(c.Env, persistence.ConfigurePostgreSqlEnv(j.platform.Spec.Services.JobService.Persistence.PostgreSql, j.GetServiceName(), j.platform.Namespace)...)
// Specific to Job Service
c.Env = append(c.Env, corev1.EnvVar{Name: quarkusFlywayMigrateAtStart, Value: "true"})
return c
}
return containerSpec
Expand All @@ -330,71 +271,6 @@ func (j JobServiceHandler) MergePodSpec(podSpec corev1.PodSpec) (corev1.PodSpec,
return *c, err
}

func (j JobServiceHandler) configurePostgreSqlEnv(postgresql *operatorapi.PersistencePostgreSql, databaseSchema, databaseNamespace string) []corev1.EnvVar {
dataSourcePort := constants.DefaultPostgreSQLPort
databaseName := "sonataflow"
dataSourceURL := postgresql.JdbcUrl
if postgresql.ServiceRef != nil {
if len(postgresql.ServiceRef.DatabaseSchema) > 0 {
databaseSchema = postgresql.ServiceRef.DatabaseSchema
}
if len(postgresql.ServiceRef.Namespace) > 0 {
databaseNamespace = postgresql.ServiceRef.Namespace
}
if postgresql.ServiceRef.Port != nil {
dataSourcePort = *postgresql.ServiceRef.Port
}
if len(postgresql.ServiceRef.DatabaseName) > 0 {
databaseName = postgresql.ServiceRef.DatabaseName
}
dataSourceURL = "jdbc:" + constants.PersistenceTypePostgreSQL + "://" + postgresql.ServiceRef.Name + "." + databaseNamespace + ":" + strconv.Itoa(dataSourcePort) + "/" + databaseName + "?currentSchema=" + databaseSchema
}

secretRef := corev1.LocalObjectReference{
Name: postgresql.SecretRef.Name,
}
quarkusDatasourceUsername := "POSTGRESQL_USER"
if len(postgresql.SecretRef.UserKey) > 0 {
quarkusDatasourceUsername = postgresql.SecretRef.UserKey
}
quarkusDatasourcePassword := "POSTGRESQL_PASSWORD"
if len(postgresql.SecretRef.PasswordKey) > 0 {
quarkusDatasourcePassword = postgresql.SecretRef.PasswordKey
}
return []corev1.EnvVar{
{
Name: "QUARKUS_DATASOURCE_USERNAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: quarkusDatasourceUsername,
LocalObjectReference: secretRef,
},
},
},
{
Name: "QUARKUS_DATASOURCE_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: quarkusDatasourcePassword,
LocalObjectReference: secretRef,
},
},
},
{
Name: "QUARKUS_DATASOURCE_DB_KIND",
Value: constants.PersistenceTypePostgreSQL,
},
{
Name: "QUARKUS_FLYWAY_MIGRATE_AT_START",
Value: "true",
},
{
Name: "QUARKUS_DATASOURCE_JDBC_URL",
Value: dataSourceURL,
},
}
}

func (j JobServiceHandler) GenerateServiceProperties() (*properties.Properties, error) {
props := properties.NewProperties()
props.Set(constants.KogitoServiceURLProperty, generateServiceURL(constants.KogitoServiceURLProtocol, j.platform.Namespace, j.GetServiceName()))
Expand Down
Loading

0 comments on commit 2b5056d

Please sign in to comment.