From 4fbbb9276776452a158714b41f82b2fb8ac48f83 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 12 Jan 2024 18:25:57 -0500 Subject: [PATCH 01/22] [KOGITO-9980] Add persistence structures in sonataflowplatform and sonataflow CRDs Signed-off-by: Jordi Gil --- api/v1alpha08/sonataflow_types.go | 3 +- api/v1alpha08/sonataflowplatform_types.go | 51 +++++++++++++ api/v1alpha08/zz_generated.deepcopy.go | 72 +++++++++++++++++++ api/zz_generated.deepcopy.go | 2 + .../sonataflow.org_sonataflowplatforms.yaml | 52 ++++++++++++++ .../manifests/sonataflow.org_sonataflows.yaml | 4 +- .../sonataflow.org_sonataflowplatforms.yaml | 52 ++++++++++++++ .../crd/bases/sonataflow.org_sonataflows.yaml | 4 +- operator.yaml | 56 ++++++++++++++- 9 files changed, 288 insertions(+), 8 deletions(-) diff --git a/api/v1alpha08/sonataflow_types.go b/api/v1alpha08/sonataflow_types.go index 37bd50f34..306ebe0fc 100644 --- a/api/v1alpha08/sonataflow_types.go +++ b/api/v1alpha08/sonataflow_types.go @@ -657,8 +657,7 @@ 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 defines the database persistence configuration for the workflow Persistence *PersistenceOptions `json:"persistence,omitempty"` } diff --git a/api/v1alpha08/sonataflowplatform_types.go b/api/v1alpha08/sonataflowplatform_types.go index 2a332b0d0..c79e2d217 100644 --- a/api/v1alpha08/sonataflowplatform_types.go +++ b/api/v1alpha08/sonataflowplatform_types.go @@ -47,6 +47,57 @@ type SonataFlowPlatformSpec struct { // +optional // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Services" Services *ServicesPlatformSpec `json:"services,omitempty"` + // Persistence defines the platform persistence configuration. When this field is set, + // the configuration is used as the persistence for platform services and sonataflow instances + // that don't provide one of their own. + // +optional + Persistence *PlatformPersistenceSpec `json:"persistence,omitempty"` +} + +// PlatformPersistenceSpec configures the DataBase support for both platform services and workflows. For services, it allows +// configuring a generic database connectivity if the service does not come with its own configured. In case of the workflow +// the configuration is defined by the image name of the supported database. When the workflow is configured to require +// a database backend type supported by the platform (that is, the platform CR contains an image name for that database type) +// , the workflow pod is configured to contain a sidecar container running the database image defined here, and the workflow +// JDBC connectivity is setup to point to the DB container. +// +optional +type PlatformPersistenceSpec struct { + // Connect configured services to a postgresql database. + // +optional + PostgreSQL *PostgreSQLPlatformSpec `json:"postgresql,omitempty"` +} + +// PostgreSQLPlatformSpec provides the generic configuration details to configure the JDBC URL and establish a connection for each managed services when they don't provide their own configuration. +type PostgreSQLPlatformSpec struct { + // SecretRef contains the database user credentials + SecretRef SecretReference `json:"secretRef"` + // ServiceRef contains the K8s service name and namespace location of the PostgreSQL service. + ServiceRef ServiceReference `json:"serviceRef,omitempty"` + // Name of postgresql database to be used. Defaults to "sonataflow" + // +kubebuilder:default:=sonataflow + DatabaseName string `json:"databaseName,omitempty"` +} + +type ServiceReference struct { + // Name contains the name of the service + // +required + Name string `json:"name"` + // Namespace contains the name of the namespace where the service is located. + // +required + Namespace string `json:"namespace"` + // Port contains the port number associated to the service. + // +required + Port int `json:"port,omitempty"` +} + +// SecretReference use of a secret to store the credentials to authenticate in the JDBC connection. +type SecretReference struct { + // Name of the postgresql credentials secret. + Name string `json:"name"` + // +optional + UserKey string `json:"userKey,omitempty"` + // +optional + PasswordKey string `json:"passwordKey,omitempty"` } // PlatformCluster is the kind of orchestration cluster the platform is installed into diff --git a/api/v1alpha08/zz_generated.deepcopy.go b/api/v1alpha08/zz_generated.deepcopy.go index d4449494f..d9a8a1ebc 100644 --- a/api/v1alpha08/zz_generated.deepcopy.go +++ b/api/v1alpha08/zz_generated.deepcopy.go @@ -360,6 +360,26 @@ func (in *PersistencePostgreSql) DeepCopy() *PersistencePostgreSql { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlatformPersistenceSpec) DeepCopyInto(out *PlatformPersistenceSpec) { + *out = *in + if in.PostgreSQL != nil { + in, out := &in.PostgreSQL, &out.PostgreSQL + *out = new(PostgreSQLPlatformSpec) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlatformPersistenceSpec. +func (in *PlatformPersistenceSpec) DeepCopy() *PlatformPersistenceSpec { + if in == nil { + return nil + } + out := new(PlatformPersistenceSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PlatformServiceRefStatus) DeepCopyInto(out *PlatformServiceRefStatus) { *out = *in @@ -585,6 +605,23 @@ func (in *PodTemplateSpec) DeepCopy() *PodTemplateSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQLPlatformSpec) DeepCopyInto(out *PostgreSQLPlatformSpec) { + *out = *in + out.SecretRef = in.SecretRef + out.ServiceRef = in.ServiceRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLPlatformSpec. +func (in *PostgreSQLPlatformSpec) DeepCopy() *PostgreSQLPlatformSpec { + if in == nil { + return nil + } + out := new(PostgreSQLPlatformSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PostgreSqlSecretOptions) DeepCopyInto(out *PostgreSqlSecretOptions) { *out = *in @@ -635,6 +672,36 @@ func (in *RegistrySpec) DeepCopy() *RegistrySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretReference) DeepCopyInto(out *SecretReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretReference. +func (in *SecretReference) DeepCopy() *SecretReference { + if in == nil { + return nil + } + out := new(SecretReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceReference) DeepCopyInto(out *ServiceReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceReference. +func (in *ServiceReference) DeepCopy() *ServiceReference { + if in == nil { + return nil + } + out := new(ServiceReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { *out = *in @@ -1032,6 +1099,11 @@ func (in *SonataFlowPlatformSpec) DeepCopyInto(out *SonataFlowPlatformSpec) { *out = new(ServicesPlatformSpec) (*in).DeepCopyInto(*out) } + if in.Persistence != nil { + in, out := &in.Persistence, &out.Persistence + *out = new(PlatformPersistenceSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SonataFlowPlatformSpec. diff --git a/api/zz_generated.deepcopy.go b/api/zz_generated.deepcopy.go index 4fd61d6d9..b68499c7b 100644 --- a/api/zz_generated.deepcopy.go +++ b/api/zz_generated.deepcopy.go @@ -20,6 +20,8 @@ package api +import () + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Condition) DeepCopyInto(out *Condition) { *out = *in diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml index 5910e8b78..4834feb6f 100644 --- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml +++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml @@ -420,6 +420,58 @@ spec: of the operator's default. type: string type: object + persistence: + description: Persistence defines the platform persistence configuration. + When this field is set, the configuration is used as the persistence + for platform services and sonataflow instances that don't provide + one of their own. + properties: + postgresql: + description: Connect configured services to a postgresql database. + properties: + databaseName: + default: sonataflow + description: Name of postgresql database to be used. Defaults + to "sonataflow" + type: string + secretRef: + description: SecretRef contains the database user credentials + properties: + name: + description: Name of the postgresql credentials secret. + This field is mandatory. + type: string + passwordKey: + type: string + userKey: + type: string + required: + - name + type: object + serviceRef: + description: ServiceRef contains the K8s service name and + namespace location of the PostgreSQL service. + properties: + name: + description: Name contains the name of the kubernetes + service. This field is mandatory. + type: string + namespace: + description: Namespace contains the name of the namespace + where the kubernetes service resides. This field is + optional. + type: string + port: + description: Port contains the port number associated + to the kubernetes service. This field is mandatory. + type: integer + required: + - name + type: object + required: + - secretRef + type: object + type: object services: description: 'Services attributes for deploying supporting applications like Data Index & Job Service. Only workflows without the `sonataflow.org/profile: diff --git a/bundle/manifests/sonataflow.org_sonataflows.yaml b/bundle/manifests/sonataflow.org_sonataflows.yaml index 13d4cf538..76c704fb3 100644 --- a/bundle/manifests/sonataflow.org_sonataflows.yaml +++ b/bundle/manifests/sonataflow.org_sonataflows.yaml @@ -2103,8 +2103,8 @@ spec: - states type: object persistence: - description: Persists service to a datasource of choice. Ephemeral - by default. + description: Persistence defines the database persistence configuration + for the workflow maxProperties: 1 properties: postgresql: diff --git a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml index 9db303538..86c1760d7 100644 --- a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml +++ b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml @@ -421,6 +421,58 @@ spec: of the operator's default. type: string type: object + persistence: + description: Persistence defines the platform persistence configuration. + When this field is set, the configuration is used as the persistence + for platform services and sonataflow instances that don't provide + one of their own. + properties: + postgresql: + description: Connect configured services to a postgresql database. + properties: + databaseName: + default: sonataflow + description: Name of postgresql database to be used. Defaults + to "sonataflow" + type: string + secretRef: + description: SecretRef contains the database user credentials + properties: + name: + description: Name of the postgresql credentials secret. + This field is mandatory. + type: string + passwordKey: + type: string + userKey: + type: string + required: + - name + type: object + serviceRef: + description: ServiceRef contains the K8s service name and + namespace location of the PostgreSQL service. + properties: + name: + description: Name contains the name of the kubernetes + service. This field is mandatory. + type: string + namespace: + description: Namespace contains the name of the namespace + where the kubernetes service resides. This field is + optional. + type: string + port: + description: Port contains the port number associated + to the kubernetes service. This field is mandatory. + type: integer + required: + - name + type: object + required: + - secretRef + type: object + type: object services: description: 'Services attributes for deploying supporting applications like Data Index & Job Service. Only workflows without the `sonataflow.org/profile: diff --git a/config/crd/bases/sonataflow.org_sonataflows.yaml b/config/crd/bases/sonataflow.org_sonataflows.yaml index 4ec54e7f8..e5214329e 100644 --- a/config/crd/bases/sonataflow.org_sonataflows.yaml +++ b/config/crd/bases/sonataflow.org_sonataflows.yaml @@ -2104,8 +2104,8 @@ spec: - states type: object persistence: - description: Persists service to a datasource of choice. Ephemeral - by default. + description: Persistence defines the database persistence configuration + for the workflow maxProperties: 1 properties: postgresql: diff --git a/operator.yaml b/operator.yaml index 74247a7de..903a7b77b 100644 --- a/operator.yaml +++ b/operator.yaml @@ -899,6 +899,58 @@ spec: of the operator's default. type: string type: object + persistence: + description: Persistence defines the platform persistence configuration. + When this field is set, the configuration is used as the persistence + for platform services and sonataflow instances that don't provide + one of their own. + properties: + postgresql: + description: Connect configured services to a postgresql database. + properties: + databaseName: + default: sonataflow + description: Name of postgresql database to be used. Defaults + to "sonataflow" + type: string + secretRef: + description: SecretRef contains the database user credentials + properties: + name: + description: Name of the postgresql credentials secret. + This field is mandatory. + type: string + passwordKey: + type: string + userKey: + type: string + required: + - name + type: object + serviceRef: + description: ServiceRef contains the K8s service name and + namespace location of the PostgreSQL service. + properties: + name: + description: Name contains the name of the kubernetes + service. This field is mandatory. + type: string + namespace: + description: Namespace contains the name of the namespace + where the kubernetes service resides. This field is + optional. + type: string + port: + description: Port contains the port number associated + to the kubernetes service. This field is mandatory. + type: integer + required: + - name + type: object + required: + - secretRef + type: object + type: object services: description: 'Services attributes for deploying supporting applications like Data Index & Job Service. Only workflows without the `sonataflow.org/profile: @@ -18860,8 +18912,8 @@ spec: - states type: object persistence: - description: Persists service to a datasource of choice. Ephemeral - by default. + description: Persistence defines the database persistence configuration + for the workflow maxProperties: 1 properties: postgresql: From 76c26aacf1d962bc82ac8d34e6acd5ffb80dcbfa Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 12 Jan 2024 18:29:52 -0500 Subject: [PATCH 02/22] Add logic for platform services and workflows to use the platform persistence configuration when they don't provide one. Services will always default to use persistence when available either within their spec or defined in the platform's. Workflows, on the other hand, can use the platform's persistence configuration by defining an empty persistence struct in their CR. Otherwise they can populate the structure with the service references Signed-off-by: Jordi Gil --- api/v1alpha08/zz_generated.deepcopy.go | 2 +- controllers/platform/initialize.go | 8 + .../services/properties_services_test.go | 18 -- controllers/platform/services/services.go | 56 ++++- .../profiles/common/object_creators.go | 28 ++- .../profiles/common/object_creators_test.go | 106 +++++++++ .../profiles/common/persistence/postgreSQL.go | 94 -------- .../profiles/common/persistence/postgresql.go | 203 +++++++++++++++++ .../sonataflowplatform_controller_test.go | 206 ++++++++++++++++++ controllers/workflows/constants.go | 24 ++ 10 files changed, 616 insertions(+), 129 deletions(-) delete mode 100644 controllers/profiles/common/persistence/postgreSQL.go create mode 100644 controllers/profiles/common/persistence/postgresql.go create mode 100644 controllers/workflows/constants.go diff --git a/api/v1alpha08/zz_generated.deepcopy.go b/api/v1alpha08/zz_generated.deepcopy.go index d9a8a1ebc..126a2b8fd 100644 --- a/api/v1alpha08/zz_generated.deepcopy.go +++ b/api/v1alpha08/zz_generated.deepcopy.go @@ -22,7 +22,7 @@ package v1alpha08 import ( "github.com/serverlessworkflow/sdk-go/v2/model" - v1 "k8s.io/api/core/v1" + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "knative.dev/pkg/apis" diff --git a/controllers/platform/initialize.go b/controllers/platform/initialize.go index 492cd0790..771eac210 100644 --- a/controllers/platform/initialize.go +++ b/controllers/platform/initialize.go @@ -30,6 +30,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/apache/incubator-kie-kogito-serverless-operator/api" + "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/persistence" "github.com/apache/incubator-kie-kogito-serverless-operator/api/metadata" @@ -106,6 +107,13 @@ func (action *initializeAction) Handle(ctx context.Context, platform *operatorap } platform.Status.Version = metadata.SpecVersion + // store workflow persistence configuration + if platform.Spec.Persistence != nil { + persistence.WorkflowConfig.SetConfig(platform.Spec.Persistence) + } else { + persistence.WorkflowConfig.SetConfig(nil) + } + return platform, nil } diff --git a/controllers/platform/services/properties_services_test.go b/controllers/platform/services/properties_services_test.go index 9cdae2e9f..90da80837 100644 --- a/controllers/platform/services/properties_services_test.go +++ b/controllers/platform/services/properties_services_test.go @@ -173,9 +173,6 @@ func generatePlatform(opts ...plfmOptionFn) *operatorapi.SonataFlowPlatform { func setJobServiceEnabledValue(v *bool) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { - if p.Spec.Services == nil { - p.Spec.Services = &operatorapi.ServicesPlatformSpec{} - } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } @@ -185,9 +182,6 @@ func setJobServiceEnabledValue(v *bool) plfmOptionFn { func setDataIndexEnabledValue(v *bool) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { - if p.Spec.Services == nil { - p.Spec.Services = &operatorapi.ServicesPlatformSpec{} - } if p.Spec.Services.DataIndex == nil { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } @@ -197,9 +191,6 @@ func setDataIndexEnabledValue(v *bool) plfmOptionFn { func emptyDataIndexServiceSpec() plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { - if p.Spec.Services == nil { - p.Spec.Services = &operatorapi.ServicesPlatformSpec{} - } if p.Spec.Services.DataIndex == nil { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } @@ -208,9 +199,6 @@ func emptyDataIndexServiceSpec() plfmOptionFn { func emptyJobServiceSpec() plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { - if p.Spec.Services == nil { - p.Spec.Services = &operatorapi.ServicesPlatformSpec{} - } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } @@ -231,9 +219,6 @@ func setPlatformName(name string) plfmOptionFn { func setJobServiceJDBC(jdbc string) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { - if p.Spec.Services == nil { - p.Spec.Services = &operatorapi.ServicesPlatformSpec{} - } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } @@ -249,9 +234,6 @@ func setJobServiceJDBC(jdbc string) plfmOptionFn { func setDataIndexJDBC(jdbc string) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { - if p.Spec.Services == nil { - p.Spec.Services = &operatorapi.ServicesPlatformSpec{} - } if p.Spec.Services.DataIndex == nil { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } diff --git a/controllers/platform/services/services.go b/controllers/platform/services/services.go index 05aedbb60..81ce3cf35 100644 --- a/controllers/platform/services/services.go +++ b/controllers/platform/services/services.go @@ -198,13 +198,25 @@ func (d DataIndexHandler) MergePodSpec(podSpec corev1.PodSpec) (corev1.PodSpec, return *c, err } +// hasPostgreSQLConfigured returns true when either the SonataFlow Platform PostgreSQL CR's structure or the one in the Data Index service specification is not nil +func (d DataIndexHandler) hasPostgreSQLConfigured() bool { + return (d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSql != nil) || + (d.platform.Spec.Persistence != nil && d.platform.Spec.Persistence.PostgreSQL != nil) +} + func (d DataIndexHandler) ConfigurePersistence(containerSpec *corev1.Container) *corev1.Container { - if d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSql != nil { + + if d.hasPostgreSQLConfigured() { + p := d.platform.Spec.Services.DataIndex.Persistence c := containerSpec.DeepCopy() c.Image = d.GetServiceImageName(constants.PersistenceTypePostgreSQL) - c.Env = append(c.Env, persistence.ConfigurePostgreSqlEnv(d.platform.Spec.Services.DataIndex.Persistence.PostgreSql, d.GetServiceName(), d.platform.Namespace)...) + if d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSql != nil { + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p.PostgreSql, d.GetServiceName(), d.platform.Namespace)...) + } else { + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnvFromPlatformSpec(d.platform.Spec.Persistence.PostgreSQL, d.GetServiceName())...) + } // specific to DataIndex - c.Env = append(c.Env, corev1.EnvVar{Name: quarkusHibernateORMDatabaseGeneration, Value: "update"}, corev1.EnvVar{Name: quarkusFlywayMigrateAtStart, Value: "true"}) + c.Env = append(c.Env, corev1.EnvVar{Name: "QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION", Value: "update"}, corev1.EnvVar{Name: "QUARKUS_FLYWAY_MIGRATE_AT_START", Value: "true"}) return c } return containerSpec @@ -358,14 +370,24 @@ func (j JobServiceHandler) MergeContainerSpec(containerSpec *corev1.Container) ( return c, err } +// hasPostgreSQLConfigured returns true when either the SonataFlow Platform PostgreSQL CR's structure or the one in the Job service specification is not nil +func (j JobServiceHandler) hasPostgreSQLConfigured() bool { + return (j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSql != nil) || + (j.platform.Spec.Persistence != nil && j.platform.Spec.Persistence.PostgreSQL != nil) +} + func (j JobServiceHandler) ConfigurePersistence(containerSpec *corev1.Container) *corev1.Container { - if j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSql != nil { + if j.hasPostgreSQLConfigured() { c := containerSpec.DeepCopy() c.Image = j.GetServiceImageName(constants.PersistenceTypePostgreSQL) - c.Env = append(c.Env, persistence.ConfigurePostgreSqlEnv(j.platform.Spec.Services.JobService.Persistence.PostgreSql, j.GetServiceName(), j.platform.Namespace)...) + if j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSql != nil { + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(j.platform.Spec.Services.JobService.Persistence.PostgreSql, j.GetServiceName(), j.platform.Namespace)...) + } else { + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnvFromPlatformSpec(j.platform.Spec.Persistence.PostgreSQL, j.GetServiceName())...) + } // Specific to Job Service - c.Env = append(c.Env, corev1.EnvVar{Name: quarkusFlywayMigrateAtStart, Value: "true"}) + c.Env = append(c.Env, corev1.EnvVar{Name: "QUARKUS_FLYWAY_MIGRATE_AT_START", Value: "true"}) return c } return containerSpec @@ -382,14 +404,26 @@ func (j JobServiceHandler) GenerateServiceProperties() (*properties.Properties, props.Set(constants.KogitoServiceURLProperty, generateServiceURL(constants.KogitoServiceURLProtocol, j.platform.Namespace, j.GetServiceName())) props.Set(constants.JobServiceKafkaSmallRyeHealthProperty, "false") // add data source reactive URL - jspec := j.platform.Spec.Services.JobService - if j.IsServiceSetInSpec() && jspec.Persistence != nil && jspec.Persistence.PostgreSql != nil { - dataSourceReactiveURL, err := generateReactiveURL(jspec.Persistence.PostgreSql, j.GetServiceName(), j.platform.Namespace, constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) - if err != nil { - return nil, err + if j.hasPostgreSQLConfigured() { + var dataSourceReactiveURL string + var err error + jspec := j.platform.Spec.Services.JobService + if j.IsServiceSetInSpec() && jspec.Persistence != nil && jspec.Persistence.PostgreSql != nil { + dataSourceReactiveURL, err = generateReactiveURL(j.platform.Spec.Services.JobService.Persistence.PostgreSql, j.GetServiceName(), j.platform.Namespace, constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) + if err != nil { + return nil, err + } + } else { + p := j.platform.Spec.Persistence.PostgreSQL + var namespace string + if len(p.ServiceRef.Namespace) > 0 { + namespace = fmt.Sprintf(".%s", p.ServiceRef.Namespace) + } + dataSourceReactiveURL = fmt.Sprintf("%s://%s%s:%d/%s?search_path=%s", constants.PersistenceTypePostgreSQL, p.ServiceRef.Name, namespace, p.ServiceRef.Port, p.DatabaseName, j.GetServiceName()) } props.Set(constants.JobServiceDataSourceReactiveURL, dataSourceReactiveURL) } + if isDataIndexEnabled(j.platform) { di := NewDataIndexHandler(j.platform) props.Set(constants.JobServiceStatusChangeEvents, "true") diff --git a/controllers/profiles/common/object_creators.go b/controllers/profiles/common/object_creators.go index 0e2d6f209..648407ee2 100644 --- a/controllers/profiles/common/object_creators.go +++ b/controllers/profiles/common/object_creators.go @@ -20,6 +20,8 @@ package common import ( + "fmt" + "github.com/imdario/mergo" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -161,7 +163,13 @@ func defaultContainer(workflow *operatorapi.SonataFlow) (*corev1.Container, erro if err := mergo.Merge(defaultFlowContainer, workflow.Spec.PodTemplate.Container.ToContainer(), mergo.WithOverride); err != nil { return nil, err } - defaultFlowContainer = ConfigurePersistence(defaultFlowContainer, workflow.Spec.Persistence, defaultSchemaName, workflow.Namespace) + if workflow.Spec.Persistence != nil { + var err error + defaultFlowContainer, err = ConfigurePersistence(defaultFlowContainer, workflow.Spec.Persistence, workflow.Name, workflow.Namespace) + if err != nil { + return nil, err + } + } // immutable defaultFlowContainer.Name = operatorapi.DefaultContainerName portIdx := -1 @@ -227,10 +235,20 @@ func ManagedPropsConfigMapCreator(workflow *operatorapi.SonataFlow, platform *op return workflowproj.CreateNewManagedPropsConfigMap(workflow, props), nil } -func ConfigurePersistence(serviceContainer *corev1.Container, options *operatorapi.PersistenceOptions, defaultSchema, namespace string) *corev1.Container { +func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistenceOptions, defaultSchema, namespace string) (*corev1.Container, error) { + if config == nil { + return serviceContainer, nil + } c := serviceContainer.DeepCopy() - if options != nil && options.PostgreSql != nil { - c.Env = append(c.Env, persistence.ConfigurePostgreSqlEnv(options.PostgreSql, defaultSchema, namespace)...) + + if config.PostgreSql != nil { + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(config.PostgreSql, defaultSchema, namespace)...) + return c, nil + } + p := persistence.WorkflowConfig.GetPostgreSQLConfiguration() + if p == nil { + return nil, fmt.Errorf("platform persistence configuration is nil") } - return c + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnvFromPlatformSpec(p, defaultSchema)...) + return c, nil } diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index ae252248e..d61960169 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -28,6 +28,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/persistence" "github.com/apache/incubator-kie-kogito-serverless-operator/utils" kubeutil "github.com/apache/incubator-kie-kogito-serverless-operator/utils/kubernetes" @@ -256,6 +257,18 @@ func TestMergePodSpec_WithPostgreSQL_and_JDBC_URL_field(t *testing.T) { Name: "QUARKUS_DATASOURCE_JDBC_URL", Value: "jdbc:postgresql://host:port/database?currentSchema=workflow", }, + { + Name: "KOGITO_PERSISTENCE_TYPE", + Value: "jdbc", + }, + { + Name: "KOGITO_PERSISTENCE_PROTO_MARSHALLER", + Value: "false", + }, + { + Name: "KOGITO_PERSISTENCE_QUERY_TIMEOUT_MILLIS", + Value: "10000", + }, } assert.Len(t, deployment.Spec.Template.Spec.Containers, 2) assert.Equal(t, "superuser", deployment.Spec.Template.Spec.ServiceAccountName) @@ -335,6 +348,18 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_and_ServiceRef(t *testin Name: "QUARKUS_DATASOURCE_JDBC_URL", Value: "jdbc:postgresql://test.foo:5432/petstore?currentSchema=bar", }, + { + Name: "KOGITO_PERSISTENCE_TYPE", + Value: "jdbc", + }, + { + Name: "KOGITO_PERSISTENCE_PROTO_MARSHALLER", + Value: "false", + }, + { + Name: "KOGITO_PERSISTENCE_QUERY_TIMEOUT_MILLIS", + Value: "10000", + }, } assert.Len(t, deployment.Spec.Template.Spec.Containers, 1) flowContainer, _ := kubeutil.GetContainerByName(v1alpha08.DefaultContainerName, &deployment.Spec.Template.Spec) @@ -342,3 +367,84 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_and_ServiceRef(t *testin assert.Equal(t, int32(8080), flowContainer.Ports[0].ContainerPort) assert.Equal(t, expectedEnvVars, flowContainer.Env) } + +func TestMergePodSpec_WithServicedPostgreSQL_From_Platform(t *testing.T) { + persistence.WorkflowConfig.SetConfig( + &v1alpha08.PlatformPersistenceSpec{ + PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ + DatabaseName: "foo", + SecretRef: v1alpha08.SecretReference{ + Name: "foo_secret", + UserKey: "username", + PasswordKey: "password", + }, + ServiceRef: v1alpha08.ServiceReference{ + Name: "service_name", + Namespace: "service_namespace", + Port: 5432, + }, + }}) + workflow := test.GetBaseSonataFlow(t.Name()) + workflow.Spec = v1alpha08.SonataFlowSpec{ + Persistence: &v1alpha08.PersistenceOptions{}, + } + object, err := DeploymentCreator(workflow) + assert.NoError(t, err) + + deployment := object.(*appsv1.Deployment) + expectedEnvVars := []corev1.EnvVar{ + { + Name: "QUARKUS_DATASOURCE_USERNAME", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "foo_secret"}, Key: "username", + }, + }, + }, + { + Name: "QUARKUS_DATASOURCE_PASSWORD", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "foo_secret"}, Key: "password", + }, + }, + }, + { + Name: "QUARKUS_DATASOURCE_DB_KIND", + Value: "postgresql", + }, + { + Name: "QUARKUS_DATASOURCE_JDBC_URL", + Value: "jdbc:postgresql://service_name.service_namespace:5432/foo?currentSchema=greeting", + }, + { + Name: "KOGITO_PERSISTENCE_TYPE", + Value: "jdbc", + }, + { + Name: "KOGITO_PERSISTENCE_PROTO_MARSHALLER", + Value: "false", + }, + { + Name: "KOGITO_PERSISTENCE_QUERY_TIMEOUT_MILLIS", + Value: "10000", + }, + } + assert.Len(t, deployment.Spec.Template.Spec.Containers, 1) + flowContainer, _ := kubeutil.GetContainerByName(v1alpha08.DefaultContainerName, &deployment.Spec.Template.Spec) + assert.Empty(t, flowContainer.Image) + assert.Equal(t, int32(8080), flowContainer.Ports[0].ContainerPort) + assert.Equal(t, expectedEnvVars, flowContainer.Env) +} +func TestMergePodSpec_WithEphemeralPostgreSQL_And_Nil_PostgreSQL_Image_In_Platform_Spec(t *testing.T) { + persistence.WorkflowConfig.SetConfig(nil) + workflow := test.GetBaseSonataFlow(t.Name()) + workflow.Spec = v1alpha08.SonataFlowSpec{ + Persistence: &v1alpha08.PersistenceOptions{}, + } + _, err := DeploymentCreator(workflow) + assert.Error(t, err) + assert.Equal(t, "platform persistence configuration is nil", err.Error()) +} diff --git a/controllers/profiles/common/persistence/postgreSQL.go b/controllers/profiles/common/persistence/postgreSQL.go deleted file mode 100644 index ebb695af6..000000000 --- a/controllers/profiles/common/persistence/postgreSQL.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2023 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 persistence - -import ( - "strconv" - - corev1 "k8s.io/api/core/v1" - - operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08" - "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants" -) - -const ( - defaultSchemaName = "default" - defaultDatabaseName = "sonataflow" - - quarkusDatasourceJDBCURL string = "QUARKUS_DATASOURCE_JDBC_URL" - quarkusDatasourceDBKind string = "QUARKUS_DATASOURCE_DB_KIND" - quarkusDatasourceUsername string = "QUARKUS_DATASOURCE_USERNAME" - quarkusDatasourcePassword string = "QUARKUS_DATASOURCE_PASSWORD" -) - -func ConfigurePostgreSqlEnv(postgresql *operatorapi.PersistencePostgreSql, databaseSchema, databaseNamespace string) []corev1.EnvVar { - dataSourcePort := constants.DefaultPostgreSQLPort - databaseName := defaultDatabaseName - 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, - } - postgresUsername := "POSTGRESQL_USER" - if len(postgresql.SecretRef.UserKey) > 0 { - postgresUsername = postgresql.SecretRef.UserKey - } - postgresPassword := "POSTGRESQL_PASSWORD" - if len(postgresql.SecretRef.PasswordKey) > 0 { - postgresPassword = postgresql.SecretRef.PasswordKey - } - return []corev1.EnvVar{ - { - Name: quarkusDatasourceUsername, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - Key: postgresUsername, - LocalObjectReference: secretRef, - }, - }, - }, - { - Name: quarkusDatasourcePassword, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - Key: postgresPassword, - LocalObjectReference: secretRef, - }, - }, - }, - { - Name: quarkusDatasourceDBKind, - Value: constants.PersistenceTypePostgreSQL, - }, - { - Name: quarkusDatasourceJDBCURL, - Value: dataSourceURL, - }, - } -} diff --git a/controllers/profiles/common/persistence/postgresql.go b/controllers/profiles/common/persistence/postgresql.go new file mode 100644 index 000000000..7098b03f2 --- /dev/null +++ b/controllers/profiles/common/persistence/postgresql.go @@ -0,0 +1,203 @@ +// Copyright 2023 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 persistence + +import ( + "fmt" + "sync" + + corev1 "k8s.io/api/core/v1" + + operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08" + "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants" +) + +const ( + defaultSchemaName = "default" + defaultDatabaseName = "sonataflow" + + timeoutSeconds = 3 + failureThreshold = 5 + initialPeriodSeconds = 15 + initialDelaySeconds = 10 + successThreshold = 1 + + postgreSQLCPULimit = "500m" + postgreSQLMemoryLimit = "256Mi" + postgreSQLMemoryRequest = "256Mi" + postgreSQLCPURequest = "100m" + + defaultPostgreSQLUsername = "sonataflow" + defaultPostgresSQLPassword = "sonataflow" +) + +var ( + WorkflowConfig *syncConfig +) + +func init() { + WorkflowConfig = newSyncConfig() +} + +type syncConfig struct { + m sync.Mutex + config *operatorapi.PlatformPersistenceSpec +} + +func newSyncConfig() *syncConfig { + return &syncConfig{m: sync.Mutex{}} +} + +func (s *syncConfig) SetConfig(config *operatorapi.PlatformPersistenceSpec) { + s.m.Lock() + defer s.m.Unlock() + s.config = config +} + +func (s *syncConfig) GetPostgreSQLConfiguration() *operatorapi.PostgreSQLPlatformSpec { + s.m.Lock() + defer s.m.Unlock() + if s.config != nil && s.config.PostgreSQL != nil { + return s.config.PostgreSQL + } + return nil +} + +func ConfigurePostgreSQLEnvFromPlatformSpec(spec *operatorapi.PostgreSQLPlatformSpec, schema string) []corev1.EnvVar { + + var namespace string + if len(spec.ServiceRef.Namespace) > 0 { + namespace = fmt.Sprintf(".%s", spec.ServiceRef.Namespace) + } + dataSourceURL := fmt.Sprintf("jdbc:postgresql://%s%s:%d/%s?currentSchema=%s", spec.ServiceRef.Name, namespace, spec.ServiceRef.Port, spec.DatabaseName, schema) + + secretRef := corev1.LocalObjectReference{ + Name: spec.SecretRef.Name, + } + quarkusDatasourceUsername := spec.SecretRef.UserKey + quarkusDatasourcePassword := spec.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_DATASOURCE_JDBC_URL", + Value: dataSourceURL, + }, + { + Name: "KOGITO_PERSISTENCE_TYPE", + Value: "jdbc", + }, + { + Name: "KOGITO_PERSISTENCE_PROTO_MARSHALLER", + Value: "false", + }, + { + Name: "KOGITO_PERSISTENCE_QUERY_TIMEOUT_MILLIS", + Value: "10000", + }, + } +} + +func ConfigurePostgreSQLEnv(postgresql *operatorapi.PersistencePostgreSql, databaseSchema, databaseNamespace string) []corev1.EnvVar { + dataSourcePort := constants.DefaultPostgreSQLPort + databaseName := defaultDatabaseName + 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 = fmt.Sprintf("jdbc:postgresql://%s.%s:%d/%s?currentSchema=%s", postgresql.ServiceRef.Name, databaseNamespace, dataSourcePort, databaseName, 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_DATASOURCE_JDBC_URL", + Value: dataSourceURL, + }, + { + Name: "KOGITO_PERSISTENCE_TYPE", + Value: "jdbc", + }, + { + Name: "KOGITO_PERSISTENCE_PROTO_MARSHALLER", + Value: "false", + }, + { + Name: "KOGITO_PERSISTENCE_QUERY_TIMEOUT_MILLIS", + Value: "10000", + }, + } +} diff --git a/controllers/sonataflowplatform_controller_test.go b/controllers/sonataflowplatform_controller_test.go index 11e5bb932..711d37fa9 100644 --- a/controllers/sonataflowplatform_controller_test.go +++ b/controllers/sonataflowplatform_controller_test.go @@ -250,6 +250,212 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, env2) }) + t.Run("verify that persistence options are correctly reconciled when defined in the platform", func(t *testing.T) { + namespace := t.Name() + // Create a SonataFlowPlatform object with metadata and spec. + ksp := test.GetBasePlatformInReadyPhase(namespace) + // Check with persistence set + ksp.Spec = v1alpha08.SonataFlowPlatformSpec{ + Services: v1alpha08.ServicesPlatformSpec{ + DataIndex: &v1alpha08.ServiceSpec{}, + JobService: &v1alpha08.ServiceSpec{}, + }, + Persistence: &v1alpha08.PlatformPersistenceSpec{ + PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ + SecretRef: v1alpha08.SecretReference{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, + ServiceRef: v1alpha08.ServiceReference{Name: "postgresql", Namespace: "default", Port: 5432}, + DatabaseName: "sonataflow", + }, + }, + } + + // Create a fake client to mock API calls. + cl := test.NewKogitoClientBuilderWithOpenShift().WithRuntimeObjects(ksp).WithStatusSubresource(ksp).Build() + // Create a SonataFlowPlatformReconciler object with the scheme and fake client. + r := &SonataFlowPlatformReconciler{cl, cl, cl.Scheme(), &rest.Config{}, &record.FakeRecorder{}} + + // Mock request to simulate Reconcile() being called on an event for a + // watched resource . + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: ksp.Name, + Namespace: ksp.Namespace, + }, + } + _, err := r.Reconcile(context.TODO(), req) + if err != nil { + t.Fatalf("reconcile: (%v)", err) + } + dbSourceKind := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_DB_KIND", + Value: constants.PersistenceTypePostgreSQL, + } + dbSourceDIURL := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_JDBC_URL", + Value: "jdbc:postgresql://postgresql.default:5432/sonataflow?currentSchema=sonataflow-platform-data-index-service", + } + dbSourceJSURL := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_JDBC_URL", + Value: "jdbc:postgresql://postgresql.default:5432/sonataflow?currentSchema=sonataflow-platform-jobs-service", + } + dbUsername := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_USERNAME", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "generic"}, + Key: "POSTGRESQL_USER", + }, + }, + } + dbPassword := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_PASSWORD", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "generic"}, + Key: "POSTGRESQL_PASSWORD", + }, + }, + } + // Check Data Index deployment to ensure it contains references to the persistence values defined in the platform CR + dep := &appsv1.Deployment{} + di := services.NewDataIndexHandler(ksp) + assert.NoError(t, cl.Get(context.TODO(), types.NamespacedName{Name: di.GetServiceName(), Namespace: ksp.Namespace}, dep)) + assert.Len(t, dep.Spec.Template.Spec.Containers, 1) + assert.Equal(t, di.GetServiceImageName(constants.PersistenceTypePostgreSQL), dep.Spec.Template.Spec.Containers[0].Image) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbSourceKind) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbUsername) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbPassword) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbSourceDIURL) + + js := services.NewJobServiceHandler(ksp) + assert.NoError(t, cl.Get(context.TODO(), types.NamespacedName{Name: js.GetServiceName(), Namespace: ksp.Namespace}, dep)) + assert.Len(t, dep.Spec.Template.Spec.Containers, 1) + assert.Equal(t, js.GetServiceImageName(constants.PersistenceTypePostgreSQL), dep.Spec.Template.Spec.Containers[0].Image) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbSourceKind) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbUsername) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbPassword) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbSourceJSURL) + }) + + t.Run("verify that persistence options are correctly reconciled when defined in the platform and overwriten in the services spec", func(t *testing.T) { + namespace := t.Name() + // Create a SonataFlowPlatform object with metadata and spec. + ksp := test.GetBasePlatformInReadyPhase(namespace) + // Check with persistence set + urlDI := "jdbc:postgresql://localhost:5432/database?currentSchema=data-index-service" + urlJS := "jdbc:postgresql://localhost:5432/database?currentSchema=job-service" + ksp.Spec = v1alpha08.SonataFlowPlatformSpec{ + Services: v1alpha08.ServicesPlatformSpec{ + DataIndex: &v1alpha08.ServiceSpec{ + Persistence: &v1alpha08.PersistenceOptions{ + PostgreSql: &v1alpha08.PersistencePostgreSql{ + SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "dataIndex"}, + JdbcUrl: urlDI, + }, + }, + }, + JobService: &v1alpha08.ServiceSpec{ + Persistence: &v1alpha08.PersistenceOptions{ + PostgreSql: &v1alpha08.PersistencePostgreSql{ + SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "job"}, + JdbcUrl: urlJS, + }, + }, + }, + }, + Persistence: &v1alpha08.PlatformPersistenceSpec{ + PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ + SecretRef: v1alpha08.SecretReference{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, + ServiceRef: v1alpha08.ServiceReference{Name: "postgresql", Namespace: "default", Port: 5432}, + DatabaseName: "sonataflow", + }, + }, + } + + // Create a fake client to mock API calls. + cl := test.NewKogitoClientBuilderWithOpenShift().WithRuntimeObjects(ksp).WithStatusSubresource(ksp).Build() + // Create a SonataFlowPlatformReconciler object with the scheme and fake client. + r := &SonataFlowPlatformReconciler{cl, cl, cl.Scheme(), &rest.Config{}, &record.FakeRecorder{}} + + // Mock request to simulate Reconcile() being called on an event for a + // watched resource . + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: ksp.Name, + Namespace: ksp.Namespace, + }, + } + _, err := r.Reconcile(context.TODO(), req) + if err != nil { + t.Fatalf("reconcile: (%v)", err) + } + dbSourceKind := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_DB_KIND", + Value: constants.PersistenceTypePostgreSQL, + } + dbDIUsername := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_USERNAME", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "dataIndex"}, + Key: "POSTGRESQL_USER", + }, + }, + } + dbDIPassword := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_PASSWORD", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "dataIndex"}, + Key: "POSTGRESQL_PASSWORD", + }, + }, + } + dbJSUsername := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_USERNAME", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "job"}, + Key: "POSTGRESQL_USER", + }, + }, + } + dbJSPassword := corev1.EnvVar{ + Name: "QUARKUS_DATASOURCE_PASSWORD", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "job"}, + Key: "POSTGRESQL_PASSWORD", + }, + }, + } + // Check Data Index deployment to ensure it contains references to the persistence values defined in the platform CR + dep := &appsv1.Deployment{} + di := services.NewDataIndexHandler(ksp) + assert.NoError(t, cl.Get(context.TODO(), types.NamespacedName{Name: di.GetServiceName(), Namespace: ksp.Namespace}, dep)) + assert.Len(t, dep.Spec.Template.Spec.Containers, 1) + assert.Equal(t, di.GetServiceImageName(constants.PersistenceTypePostgreSQL), dep.Spec.Template.Spec.Containers[0].Image) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbSourceKind) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbDIUsername) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbDIPassword) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "QUARKUS_DATASOURCE_JDBC_URL", Value: urlDI}) + + js := services.NewJobServiceHandler(ksp) + assert.NoError(t, cl.Get(context.TODO(), types.NamespacedName{Name: js.GetServiceName(), Namespace: ksp.Namespace}, dep)) + assert.Len(t, dep.Spec.Template.Spec.Containers, 1) + assert.Equal(t, js.GetServiceImageName(constants.PersistenceTypePostgreSQL), dep.Spec.Template.Spec.Containers[0].Image) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbSourceKind) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbJSUsername) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, dbJSPassword) + assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "QUARKUS_DATASOURCE_JDBC_URL", Value: urlJS}) + }) + // Job Service tests t.Run("verify that a basic reconcile with job service & persistence is performed without error", func(t *testing.T) { namespace := t.Name() diff --git a/controllers/workflows/constants.go b/controllers/workflows/constants.go new file mode 100644 index 000000000..afcf157a1 --- /dev/null +++ b/controllers/workflows/constants.go @@ -0,0 +1,24 @@ +// 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 workflows + +const ( + QuarkusFlywayMigrateAtStart string = "quarkus.flyway.migrate-at-start" + QuarkusDatasourceJDBCURL string = "quarkus.datasource.jdbc.url" + KogitoPersistenceType string = "kogito.persistence.type" + JDBCPersistenceType string = "jdbc" + KogitoPersistenceQueryTimeoutMillis string = "kogito.persistence.query.timeout.millis" + KogitoPersistenceProtoMarshaller string = "kogito.persistence.proto.marshaller" +) From 55b9cc6468a4078a92e5a204af00e737c256d212 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 12 Jan 2024 18:31:22 -0500 Subject: [PATCH 03/22] Added e2e tests for both platform and workflows leveraging on their own provided configuration or one derived from the platform Signed-off-by: Jordi Gil --- test/e2e/helpers.go | 24 +++++ test/e2e/platform_test.go | 67 +++++++++++- .../generic_from_platform_cr/01-postgres.yaml | 86 +++++++++++++++ .../02-sonataflow_platform.yaml | 52 +++++++++ .../kustomization.yaml | 32 ++++++ .../overwritten_by_services/01-postgres.yaml | 86 +++++++++++++++ .../02-sonataflow_platform.yaml | 71 ++++++++++++ .../kustomization.yaml | 32 ++++++ .../persistence/by_service/01-postgres.yaml | 86 +++++++++++++++ .../by_service/02-sonataflow_platform.yaml | 27 +++++ ...3-sonataflow_callbackstatetimeouts.sw.yaml | 102 ++++++++++++++++++ .../persistence/by_service/kustomization.yaml | 33 ++++++ .../from_platform/01-postgres.yaml | 86 +++++++++++++++ .../from_platform/02-sonataflow_platform.yaml | 38 +++++++ ...3-sonataflow_callbackstatetimeouts.sw.yaml | 86 +++++++++++++++ .../from_platform/kustomization.yaml | 33 ++++++ test/yaml.go | 8 ++ 17 files changed, 947 insertions(+), 2 deletions(-) create mode 100644 test/testdata/platform/persistence/generic_from_platform_cr/01-postgres.yaml create mode 100644 test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml create mode 100644 test/testdata/platform/persistence/generic_from_platform_cr/kustomization.yaml create mode 100644 test/testdata/platform/persistence/overwritten_by_services/01-postgres.yaml create mode 100644 test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml create mode 100644 test/testdata/platform/persistence/overwritten_by_services/kustomization.yaml create mode 100644 test/testdata/workflow/persistence/by_service/01-postgres.yaml create mode 100644 test/testdata/workflow/persistence/by_service/02-sonataflow_platform.yaml create mode 100644 test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml create mode 100644 test/testdata/workflow/persistence/by_service/kustomization.yaml create mode 100644 test/testdata/workflow/persistence/from_platform/01-postgres.yaml create mode 100644 test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml create mode 100644 test/testdata/workflow/persistence/from_platform/03-sonataflow_callbackstatetimeouts.sw.yaml create mode 100644 test/testdata/workflow/persistence/from_platform/kustomization.yaml diff --git a/test/e2e/helpers.go b/test/e2e/helpers.go index 0b87fa894..d1c344a3a 100644 --- a/test/e2e/helpers.go +++ b/test/e2e/helpers.go @@ -161,6 +161,30 @@ func verifyWorkflowIsAddressable(workflowName string, targetNamespace string) bo } } +func verifyDatabaseConnectionsHealthStatusInPod(name string, namespace string) { + // iterate over all containers to find the one that responds to the HTTP health endpoint + Expect(name).NotTo(BeEmpty(), "pod name is empty") + cmd := exec.Command("kubectl", "get", "pod", name, "-n", namespace, "-o", `jsonpath={.spec.containers[*].name}`) + output, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred()) + var errs error + for _, cname := range strings.Split(string(output), " ") { + var h *health + h, err = getHealthStatusInContainer(name, cname, namespace) + if err == nil { + for _, c := range h.Checks { + if c.Name == "Database connections health check" { + Expect(c.Status).To(Equal(upStatus)) + return + } + } + errs = fmt.Errorf("no database connection health status found; %w", errs) + } + errs = fmt.Errorf("%v; %w", err, errs) + } + Expect(errs).NotTo(HaveOccurred(), fmt.Sprintf("No container was found that could respond to the health endpoint %v", errs)) +} + const ( minikubePlatform = "minikube" openshiftPlatform = "openshift" diff --git a/test/e2e/platform_test.go b/test/e2e/platform_test.go index a439e3d6d..125dd01bd 100644 --- a/test/e2e/platform_test.go +++ b/test/e2e/platform_test.go @@ -15,8 +15,6 @@ package e2e import ( - //nolint:golint - //nolint:revive "bytes" "fmt" "math/rand" @@ -123,4 +121,69 @@ var _ = Describe("Validate the persistence", Ordered, func() { ) }) + + DescribeTable("when deploying a SonataFlowPlatform CR with PostgreSQL Persistence", func(testcaseDir string) { + By("Deploy the CR") + var manifests []byte + EventuallyWithOffset(1, func() error { + var err error + cmd := exec.Command("kubectl", "kustomize", testcaseDir) + manifests, err = utils.Run(cmd) + return err + }, time.Minute, time.Second).Should(Succeed()) + cmd := exec.Command("kubectl", "create", "-n", targetNamespace, "-f", "-") + cmd.Stdin = bytes.NewBuffer(manifests) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred()) + By("Wait for SonatatFlowPlatform CR to complete deployment") + // wait for service deployments to be ready + EventuallyWithOffset(1, func() error { + cmd = exec.Command("kubectl", "wait", "pod", "-n", targetNamespace, "-l", "app=sonataflow-platform", "--for", "condition=Ready", "--timeout=5s") + _, err = utils.Run(cmd) + return err + }, 10*time.Minute, 5).Should(Succeed()) + By("Evaluate status of all service's health endpoint") + cmd = exec.Command("kubectl", "get", "pod", "-l", "app=sonataflow-platform", "-n", targetNamespace, "-ojsonpath={.items[*].metadata.name}") + output, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred()) + for _, pn := range strings.Split(string(output), " ") { + verifyHealthStatusInPod(pn, targetNamespace) + } + }, + Entry("and both Job Service and Data Index using the persistence from platform CR", test.GetSonataFlowE2EPlatformPersistenceSampleDataDirectory("generic_from_platform_cr")), + Entry("and both Job Service and Data Index using the one defined in each service, discarding the one from the platform CR", test.GetSonataFlowE2EPlatformPersistenceSampleDataDirectory("overwritten_by_services")), + ) + + DescribeTable("when deploying a SonataFlow CR with PostgreSQL persistence", func(testcaseDir string) { + By("Deploy the CR") + var manifests []byte + EventuallyWithOffset(1, func() error { + var err error + cmd := exec.Command("kubectl", "kustomize", testcaseDir) + manifests, err = utils.Run(cmd) + return err + }, time.Minute, time.Second).Should(Succeed()) + cmd := exec.Command("kubectl", "create", "-n", targetNamespace, "-f", "-") + cmd.Stdin = bytes.NewBuffer(manifests) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred()) + By("Wait for SonatatFlow CR to complete deployment") + // wait for service deployments to be ready + EventuallyWithOffset(1, func() error { + cmd = exec.Command("kubectl", "wait", "pod", "-n", targetNamespace, "-l", "sonataflow.org/workflow-app=callbackstatetimeouts", "--for", "condition=Ready", "--timeout=5s") + _, err = utils.Run(cmd) + return err + }, 10*time.Minute, 5).Should(Succeed()) + By("Evaluate status of the workflow's pod health endpoint") + cmd = exec.Command("kubectl", "get", "pod", "-l", "sonataflow.org/workflow-app=callbackstatetimeouts", "-n", targetNamespace, "-ojsonpath={.items[*].metadata.name}") + output, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred()) + for _, pn := range strings.Split(string(output), " ") { + verifyDatabaseConnectionsHealthStatusInPod(pn, targetNamespace) + } + }, + Entry("defined in the workflow from an existing kubernetes service as a reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("by_service")), + Entry("defined in the workflow derived from the workflow platform CR", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform")), + ) + }) diff --git a/test/testdata/platform/persistence/generic_from_platform_cr/01-postgres.yaml b/test/testdata/platform/persistence/generic_from_platform_cr/01-postgres.yaml new file mode 100644 index 000000000..662de4c7b --- /dev/null +++ b/test/testdata/platform/persistence/generic_from_platform_cr/01-postgres.yaml @@ -0,0 +1,86 @@ +# 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. + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: postgres + template: + metadata: + labels: + app.kubernetes.io/name: postgres + spec: + containers: + - name: postgres + image: postgres:13.2-alpine + imagePullPolicy: 'IfNotPresent' + ports: + - containerPort: 5432 + volumeMounts: + - name: storage + mountPath: /var/lib/postgresql/data + envFrom: + - secretRef: + name: postgres-secrets + readinessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + livenessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + resources: + limits: + memory: "256Mi" + cpu: "500m" + volumes: + - name: storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + selector: + app.kubernetes.io/name: postgres + ports: + - port: 5432 diff --git a/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml new file mode 100644 index 000000000..ef77a31d9 --- /dev/null +++ b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml @@ -0,0 +1,52 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform +spec: + build: + template: + buildArgs: + - name: QUARKUS_EXTENSIONS + value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:2.0.0-SNAPSHOT + persistence: + postgresql: + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: postgres + namespace: default + port: 5432 + databaseName: sonataflow + services: + dataIndex: + enabled: false + podTemplate: + initContainers: + - name: init-postgres + image: registry.access.redhat.com/ubi9/ubi-minimal: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;' ] + jobService: + enabled: false + podTemplate: + initContainers: + - name: init-postgres + image: registry.access.redhat.com/ubi9/ubi-minimal: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;' ] diff --git a/test/testdata/platform/persistence/generic_from_platform_cr/kustomization.yaml b/test/testdata/platform/persistence/generic_from_platform_cr/kustomization.yaml new file mode 100644 index 000000000..d3fd127c7 --- /dev/null +++ b/test/testdata/platform/persistence/generic_from_platform_cr/kustomization.yaml @@ -0,0 +1,32 @@ +# 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. + +resources: +- 01-postgres.yaml +- 02-sonataflow_platform.yaml + +generatorOptions: + disableNameSuffixHash: true + +secretGenerator: + - name: postgres-secrets + literals: + - POSTGRES_USER=sonataflow + - POSTGRES_PASSWORD=sonataflow + - POSTGRES_DATABASE=sonataflow + - PGDATA=/var/lib/pgsql/data/userdata + +sortOptions: + order: fifo + diff --git a/test/testdata/platform/persistence/overwritten_by_services/01-postgres.yaml b/test/testdata/platform/persistence/overwritten_by_services/01-postgres.yaml new file mode 100644 index 000000000..662de4c7b --- /dev/null +++ b/test/testdata/platform/persistence/overwritten_by_services/01-postgres.yaml @@ -0,0 +1,86 @@ +# 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. + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: postgres + template: + metadata: + labels: + app.kubernetes.io/name: postgres + spec: + containers: + - name: postgres + image: postgres:13.2-alpine + imagePullPolicy: 'IfNotPresent' + ports: + - containerPort: 5432 + volumeMounts: + - name: storage + mountPath: /var/lib/postgresql/data + envFrom: + - secretRef: + name: postgres-secrets + readinessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + livenessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + resources: + limits: + memory: "256Mi" + cpu: "500m" + volumes: + - name: storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + selector: + app.kubernetes.io/name: postgres + ports: + - port: 5432 diff --git a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml new file mode 100644 index 000000000..41026d89f --- /dev/null +++ b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml @@ -0,0 +1,71 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform +spec: + build: + template: + buildArgs: + - name: QUARKUS_EXTENSION + value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:2.0.0-SNAPSHOT + config: + strategyOptions: + KanikoBuildCacheEnabled: "true" + persistence: + services: + postgresql: + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: postgres + namespace: default + port: 5432 + databaseName: sonataflow + services: + dataIndex: + enabled: false + persistence: + postgresql: + jdbcUrl: jdbc:postgresql://postgres:5432/sonataflow?currentSchema=data-index-service + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + podTemplate: + initContainers: + - name: init-postgres + image: registry.access.redhat.com/ubi9/ubi-minimal: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;' ] + jobService: + enabled: false + persistence: + postgresql: + jdbcUrl: jdbc:postgresql://postgres:5432/sonataflow?currentSchema=jobs-service + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + podTemplate: + initContainers: + - name: init-postgres + image: registry.access.redhat.com/ubi9/ubi-minimal: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;' ] + diff --git a/test/testdata/platform/persistence/overwritten_by_services/kustomization.yaml b/test/testdata/platform/persistence/overwritten_by_services/kustomization.yaml new file mode 100644 index 000000000..d3fd127c7 --- /dev/null +++ b/test/testdata/platform/persistence/overwritten_by_services/kustomization.yaml @@ -0,0 +1,32 @@ +# 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. + +resources: +- 01-postgres.yaml +- 02-sonataflow_platform.yaml + +generatorOptions: + disableNameSuffixHash: true + +secretGenerator: + - name: postgres-secrets + literals: + - POSTGRES_USER=sonataflow + - POSTGRES_PASSWORD=sonataflow + - POSTGRES_DATABASE=sonataflow + - PGDATA=/var/lib/pgsql/data/userdata + +sortOptions: + order: fifo + diff --git a/test/testdata/workflow/persistence/by_service/01-postgres.yaml b/test/testdata/workflow/persistence/by_service/01-postgres.yaml new file mode 100644 index 000000000..662de4c7b --- /dev/null +++ b/test/testdata/workflow/persistence/by_service/01-postgres.yaml @@ -0,0 +1,86 @@ +# 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. + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: postgres + template: + metadata: + labels: + app.kubernetes.io/name: postgres + spec: + containers: + - name: postgres + image: postgres:13.2-alpine + imagePullPolicy: 'IfNotPresent' + ports: + - containerPort: 5432 + volumeMounts: + - name: storage + mountPath: /var/lib/postgresql/data + envFrom: + - secretRef: + name: postgres-secrets + readinessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + livenessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + resources: + limits: + memory: "256Mi" + cpu: "500m" + volumes: + - name: storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + selector: + app.kubernetes.io/name: postgres + ports: + - port: 5432 diff --git a/test/testdata/workflow/persistence/by_service/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/by_service/02-sonataflow_platform.yaml new file mode 100644 index 000000000..5867f2d60 --- /dev/null +++ b/test/testdata/workflow/persistence/by_service/02-sonataflow_platform.yaml @@ -0,0 +1,27 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform +spec: + build: + template: + buildArgs: + - name: QUARKUS_EXTENSIONS + value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:999-SNAPSHOT,org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final + config: + strategyOptions: + KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml new file mode 100644 index 000000000..4c1e0fb9d --- /dev/null +++ b/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml @@ -0,0 +1,102 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlow +metadata: + name: callbackstatetimeouts + annotations: + sonataflow.org/description: Callback State Timeouts Example k8s + sonataflow.org/version: 0.0.1 +spec: + persistence: + postgresql: + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: postgres + namespace: default + port: 5432 + databaseName: sonataflow + databaseSchema: callbackstatetimeouts + podTemplate: + container: + env: + - name: QUARKUS_FLYWAY_MIGRATE_AT_START + value: "true" + initContainers: + - name: init-postgres + image: registry.access.redhat.com/ubi9/ubi-minimal: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;' ] + flow: + start: PrintStartMessage + events: + - name: callbackEvent + source: '' + type: callback_event_type + functions: + - name: systemOut + type: custom + operation: sysout + states: + - name: PrintStartMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has started.\"}" + transition: CallbackState + - name: CallbackState + type: callback + action: + name: callbackAction + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has executed the callbackFunction.\"}" + eventRef: callbackEvent + transition: CheckEventArrival + timeouts: + eventTimeout: PT30S + - name: CheckEventArrival + type: switch + dataConditions: + - condition: "${ .eventData != null }" + transition: EventArrived + defaultCondition: + transition: EventNotArrived + - name: EventArrived + type: inject + data: + exitMessage: "The callback event has arrived." + transition: PrintExitMessage + - name: EventNotArrived + type: inject + data: + exitMessage: "The callback event has not arrived, and the timeout has overdue." + transition: PrintExitMessage + - name: PrintExitMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has finalized. \" + .exitMessage + \" eventData: \" + .eventData}" + end: true diff --git a/test/testdata/workflow/persistence/by_service/kustomization.yaml b/test/testdata/workflow/persistence/by_service/kustomization.yaml new file mode 100644 index 000000000..b7f587bcc --- /dev/null +++ b/test/testdata/workflow/persistence/by_service/kustomization.yaml @@ -0,0 +1,33 @@ +# 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. + +resources: +- 01-postgres.yaml +- 02-sonataflow_platform.yaml +- 03-sonataflow_callbackstatetimeouts.sw.yaml + +generatorOptions: + disableNameSuffixHash: true + +secretGenerator: + - name: postgres-secrets + literals: + - POSTGRES_USER=sonataflow + - POSTGRES_PASSWORD=sonataflow + - POSTGRES_DATABASE=sonataflow + - PGDATA=/var/lib/pgsql/data/userdata + +sortOptions: + order: fifo + diff --git a/test/testdata/workflow/persistence/from_platform/01-postgres.yaml b/test/testdata/workflow/persistence/from_platform/01-postgres.yaml new file mode 100644 index 000000000..662de4c7b --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform/01-postgres.yaml @@ -0,0 +1,86 @@ +# 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. + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: postgres + template: + metadata: + labels: + app.kubernetes.io/name: postgres + spec: + containers: + - name: postgres + image: postgres:13.2-alpine + imagePullPolicy: 'IfNotPresent' + ports: + - containerPort: 5432 + volumeMounts: + - name: storage + mountPath: /var/lib/postgresql/data + envFrom: + - secretRef: + name: postgres-secrets + readinessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + livenessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + resources: + limits: + memory: "256Mi" + cpu: "500m" + volumes: + - name: storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + selector: + app.kubernetes.io/name: postgres + ports: + - port: 5432 diff --git a/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml new file mode 100644 index 000000000..34c40cbae --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml @@ -0,0 +1,38 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform +spec: + persistence: + postgresql: + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: postgres + namespace: default + port: 5432 + databaseName: sonataflow + build: + template: + buildArgs: + - name: QUARKUS_EXTENSIONS + value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:999-SNAPSHOT,org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final + config: + strategyOptions: + KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/from_platform/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/workflow/persistence/from_platform/03-sonataflow_callbackstatetimeouts.sw.yaml new file mode 100644 index 000000000..de52b153d --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform/03-sonataflow_callbackstatetimeouts.sw.yaml @@ -0,0 +1,86 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlow +metadata: + name: callbackstatetimeouts + annotations: + sonataflow.org/description: Callback State Timeouts Example k8s + sonataflow.org/version: 0.0.1 +spec: + persistence: {} + podTemplate: + container: + env: + - name: QUARKUS_FLYWAY_MIGRATE_AT_START + value: "true" + flow: + start: PrintStartMessage + events: + - name: callbackEvent + source: '' + type: callback_event_type + functions: + - name: systemOut + type: custom + operation: sysout + states: + - name: PrintStartMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has started.\"}" + transition: CallbackState + - name: CallbackState + type: callback + action: + name: callbackAction + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has executed the callbackFunction.\"}" + eventRef: callbackEvent + transition: CheckEventArrival + timeouts: + eventTimeout: PT30S + - name: CheckEventArrival + type: switch + dataConditions: + - condition: "${ .eventData != null }" + transition: EventArrived + defaultCondition: + transition: EventNotArrived + - name: EventArrived + type: inject + data: + exitMessage: "The callback event has arrived." + transition: PrintExitMessage + - name: EventNotArrived + type: inject + data: + exitMessage: "The callback event has not arrived, and the timeout has overdue." + transition: PrintExitMessage + - name: PrintExitMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has finalized. \" + .exitMessage + \" eventData: \" + .eventData}" + end: true diff --git a/test/testdata/workflow/persistence/from_platform/kustomization.yaml b/test/testdata/workflow/persistence/from_platform/kustomization.yaml new file mode 100644 index 000000000..b7f587bcc --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform/kustomization.yaml @@ -0,0 +1,33 @@ +# 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. + +resources: +- 01-postgres.yaml +- 02-sonataflow_platform.yaml +- 03-sonataflow_callbackstatetimeouts.sw.yaml + +generatorOptions: + disableNameSuffixHash: true + +secretGenerator: + - name: postgres-secrets + literals: + - POSTGRES_USER=sonataflow + - POSTGRES_PASSWORD=sonataflow + - POSTGRES_DATABASE=sonataflow + - PGDATA=/var/lib/pgsql/data/userdata + +sortOptions: + order: fifo + diff --git a/test/yaml.go b/test/yaml.go index d1c4006b9..c769f4812 100644 --- a/test/yaml.go +++ b/test/yaml.go @@ -266,6 +266,14 @@ func GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory(subdir string) strin return filepath.Join(getTestDataDir(), "persistence", "workflow", subdir) } +func GetSonataFlowE2EPlatformPersistenceSampleDataDirectory(subdir string) string { + return filepath.Join(getTestDataDir(), "platform", "persistence", subdir) +} + +func GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory(subdir string) string { + return filepath.Join(getTestDataDir(), "workflow", "persistence", subdir) +} + // getTestDataDir gets the testdata directory containing every sample out there from test/testdata. // It should be used for every testing unit within the module. func getTestDataDir() string { From 4757a09d04c63e5c9ed9ea30a1899c6ad8510087 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 26 Jan 2024 10:00:03 -0500 Subject: [PATCH 04/22] Change namespace in persistence service reference to be optional to avoid requiting to provide it when the service is co-located to the sonataflow CR Signed-off-by: Jordi Gil --- api/v1alpha08/sonataflowplatform_types.go | 10 +++++----- .../profiles/common/object_creators.go | 2 +- .../profiles/common/object_creators_test.go | 4 ++-- .../02-sonataflow_platform.yaml | 1 - .../02-sonataflow_platform.yaml | 20 +++++++++---------- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/api/v1alpha08/sonataflowplatform_types.go b/api/v1alpha08/sonataflowplatform_types.go index c79e2d217..f71a88256 100644 --- a/api/v1alpha08/sonataflowplatform_types.go +++ b/api/v1alpha08/sonataflowplatform_types.go @@ -79,20 +79,20 @@ type PostgreSQLPlatformSpec struct { } type ServiceReference struct { - // Name contains the name of the service + // Name contains the name of the kubernetes service. This field is mandatory. // +required Name string `json:"name"` - // Namespace contains the name of the namespace where the service is located. - // +required + // Namespace contains the name of the namespace where the kubernetes service resides. This field is optional. + // +optional Namespace string `json:"namespace"` - // Port contains the port number associated to the service. + // Port contains the port number associated to the kubernetes service. This field is mandatory. // +required Port int `json:"port,omitempty"` } // SecretReference use of a secret to store the credentials to authenticate in the JDBC connection. type SecretReference struct { - // Name of the postgresql credentials secret. + // Name of the postgresql credentials secret. This field is mandatory. Name string `json:"name"` // +optional UserKey string `json:"userKey,omitempty"` diff --git a/controllers/profiles/common/object_creators.go b/controllers/profiles/common/object_creators.go index 648407ee2..33bff76f0 100644 --- a/controllers/profiles/common/object_creators.go +++ b/controllers/profiles/common/object_creators.go @@ -247,7 +247,7 @@ func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorap } p := persistence.WorkflowConfig.GetPostgreSQLConfiguration() if p == nil { - return nil, fmt.Errorf("platform persistence configuration is nil") + return nil, fmt.Errorf("platform persistence configuration is undefined") } c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnvFromPlatformSpec(p, defaultSchema)...) return c, nil diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index d61960169..7a21b87bb 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -438,7 +438,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_From_Platform(t *testing.T) { assert.Equal(t, int32(8080), flowContainer.Ports[0].ContainerPort) assert.Equal(t, expectedEnvVars, flowContainer.Env) } -func TestMergePodSpec_WithEphemeralPostgreSQL_And_Nil_PostgreSQL_Image_In_Platform_Spec(t *testing.T) { +func TestMergePodSpec_WithEphemeralPostgreSQL_And_Undefined_PostgreSQL_Image_In_Platform_Spec(t *testing.T) { persistence.WorkflowConfig.SetConfig(nil) workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ @@ -446,5 +446,5 @@ func TestMergePodSpec_WithEphemeralPostgreSQL_And_Nil_PostgreSQL_Image_In_Platfo } _, err := DeploymentCreator(workflow) assert.Error(t, err) - assert.Equal(t, "platform persistence configuration is nil", err.Error()) + assert.Equal(t, "platform persistence configuration is undefined", err.Error()) } diff --git a/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml index ef77a31d9..1dfbf4a7f 100644 --- a/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml +++ b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml @@ -30,7 +30,6 @@ spec: passwordKey: POSTGRES_PASSWORD serviceRef: name: postgres - namespace: default port: 5432 databaseName: sonataflow services: diff --git a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml index 41026d89f..3bca4cd73 100644 --- a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml +++ b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml @@ -26,17 +26,15 @@ spec: strategyOptions: KanikoBuildCacheEnabled: "true" persistence: - services: - postgresql: - secretRef: - name: postgres-secrets - userKey: POSTGRES_USER - passwordKey: POSTGRES_PASSWORD - serviceRef: - name: postgres - namespace: default - port: 5432 - databaseName: sonataflow + postgresql: + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: postgres + port: 5432 + databaseName: sonataflow services: dataIndex: enabled: false From 705846b2e8069105654a2e6a524747ad8edfe901 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 26 Jan 2024 11:14:11 -0500 Subject: [PATCH 05/22] Extend e2e tests to cover for workflow CR using platform CR to provide persistence configuration Signed-off-by: Jordi Gil --- .../profiles/common/object_creators_test.go | 134 +++++++++++++++++- test/e2e/helpers.go | 1 + test/e2e/workflow_test.go | 2 + .../02-sonataflow_platform.yaml | 8 +- ...3-sonataflow_callbackstatetimeouts.sw.yaml | 1 - .../from_platform/02-sonataflow_platform.yaml | 1 - .../01-postgres.yaml | 86 +++++++++++ .../02-sonataflow_platform.yaml | 37 +++++ ...3-sonataflow_callbackstatetimeouts.sw.yaml | 101 +++++++++++++ .../kustomization.yaml | 33 +++++ test/yaml.go | 3 - 11 files changed, 396 insertions(+), 11 deletions(-) create mode 100644 test/testdata/workflow/persistence/from_platform_overwritten_by_service/01-postgres.yaml create mode 100644 test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml create mode 100644 test/testdata/workflow/persistence/from_platform_overwritten_by_service/03-sonataflow_callbackstatetimeouts.sw.yaml create mode 100644 test/testdata/workflow/persistence/from_platform_overwritten_by_service/kustomization.yaml diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index 7a21b87bb..6720c6234 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -284,7 +284,7 @@ var ( postgreSQLPort = 5432 ) -func TestMergePodSpec_OverrideContainers_WithPostgreSQL_and_ServiceRef(t *testing.T) { +func TestMergePodSpec_OverrideContainers_WithPostgreSQL_In_Workflow_CR(t *testing.T) { workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ PodTemplate: v1alpha08.PodTemplateSpec{ @@ -368,7 +368,7 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_and_ServiceRef(t *testin assert.Equal(t, expectedEnvVars, flowContainer.Env) } -func TestMergePodSpec_WithServicedPostgreSQL_From_Platform(t *testing.T) { +func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesting_It(t *testing.T) { persistence.WorkflowConfig.SetConfig( &v1alpha08.PlatformPersistenceSpec{ PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ @@ -438,6 +438,136 @@ func TestMergePodSpec_WithServicedPostgreSQL_From_Platform(t *testing.T) { assert.Equal(t, int32(8080), flowContainer.Ports[0].ContainerPort) assert.Equal(t, expectedEnvVars, flowContainer.Env) } + +func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *testing.T) { + persistence.WorkflowConfig.SetConfig( + &v1alpha08.PlatformPersistenceSpec{ + PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ + DatabaseName: "foo", + SecretRef: v1alpha08.SecretReference{ + Name: "foo_secret", + UserKey: "username", + PasswordKey: "password", + }, + ServiceRef: v1alpha08.ServiceReference{ + Name: "service_name", + Namespace: "service_namespace", + Port: 5432, + }, + }}) + workflow := test.GetBaseSonataFlow(t.Name()) + workflow.Spec = v1alpha08.SonataFlowSpec{ + PodTemplate: v1alpha08.PodTemplateSpec{ + PodSpec: v1alpha08.PodSpec{ + // Try to override the workflow container via the podspec + Containers: []corev1.Container{ + { + Name: v1alpha08.DefaultContainerName, + Image: "quay.io/example/my-workflow:1.0.0", + Ports: []corev1.ContainerPort{ + {Name: utils.HttpScheme, ContainerPort: 9090}, + }, + Env: []corev1.EnvVar{ + {Name: "ENV1", Value: "VALUE_CUSTOM"}, + }, + }, + }, + }, + }, + Persistence: &v1alpha08.PersistenceOptions{ + PostgreSql: &v1alpha08.PersistencePostgreSql{ + SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "test"}, + ServiceRef: &v1alpha08.PostgreSqlServiceOptions{ + Name: "test", + Namespace: "default", + Port: &postgreSQLPort, + DatabaseName: "my_database", + DatabaseSchema: "bar"}, + }, + }, + } + object, err := DeploymentCreator(workflow) + assert.NoError(t, err) + + deployment := object.(*appsv1.Deployment) + expectedEnvVars := []corev1.EnvVar{ + { + Name: "QUARKUS_DATASOURCE_USERNAME", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "test"}, Key: "POSTGRESQL_USER", + }, + }, + }, + { + Name: "QUARKUS_DATASOURCE_PASSWORD", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "test"}, Key: "POSTGRESQL_PASSWORD", + }, + }, + }, + { + Name: "QUARKUS_DATASOURCE_DB_KIND", + Value: "postgresql", + }, + { + Name: "QUARKUS_DATASOURCE_JDBC_URL", + Value: "jdbc:postgresql://test.default:5432/my_database?currentSchema=bar", + }, + { + Name: "KOGITO_PERSISTENCE_TYPE", + Value: "jdbc", + }, + { + Name: "KOGITO_PERSISTENCE_PROTO_MARSHALLER", + Value: "false", + }, + { + Name: "KOGITO_PERSISTENCE_QUERY_TIMEOUT_MILLIS", + Value: "10000", + }, + } + assert.Len(t, deployment.Spec.Template.Spec.Containers, 1) + flowContainer, _ := kubeutil.GetContainerByName(v1alpha08.DefaultContainerName, &deployment.Spec.Template.Spec) + assert.Empty(t, flowContainer.Image) + assert.Equal(t, int32(8080), flowContainer.Ports[0].ContainerPort) + assert.Equal(t, expectedEnvVars, flowContainer.Env) +} + +func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Requesting_it(t *testing.T) { + persistence.WorkflowConfig.SetConfig( + &v1alpha08.PlatformPersistenceSpec{ + PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ + DatabaseName: "foo", + SecretRef: v1alpha08.SecretReference{ + Name: "foo_secret", + UserKey: "username", + PasswordKey: "password", + }, + ServiceRef: v1alpha08.ServiceReference{ + Name: "service_name", + Namespace: "service_namespace", + Port: 5432, + }, + }}) + workflow := test.GetBaseSonataFlow(t.Name()) + workflow.Spec = v1alpha08.SonataFlowSpec{ + Persistence: nil, + } + object, err := DeploymentCreator(workflow) + assert.NoError(t, err) + + deployment := object.(*appsv1.Deployment) + assert.Len(t, deployment.Spec.Template.Spec.Containers, 1) + flowContainer, _ := kubeutil.GetContainerByName(v1alpha08.DefaultContainerName, &deployment.Spec.Template.Spec) + assert.Empty(t, flowContainer.Image) + assert.Equal(t, int32(8080), flowContainer.Ports[0].ContainerPort) + assert.Nil(t, flowContainer.Env) +} + func TestMergePodSpec_WithEphemeralPostgreSQL_And_Undefined_PostgreSQL_Image_In_Platform_Spec(t *testing.T) { persistence.WorkflowConfig.SetConfig(nil) workflow := test.GetBaseSonataFlow(t.Name()) diff --git a/test/e2e/helpers.go b/test/e2e/helpers.go index d1c344a3a..88fffc314 100644 --- a/test/e2e/helpers.go +++ b/test/e2e/helpers.go @@ -114,6 +114,7 @@ func getHealthStatusInContainer(podName string, containerName string, ns string) if err != nil { return nil, fmt.Errorf("failed to execute curl command against health endpoint in container %s:%v with output %s", containerName, err, output) } + GinkgoWriter.Println(fmt.Sprintf("Health status:\n%s", string(output))) return &h, nil } func verifyWorkflowIsInRunningStateInNamespace(workflowName string, ns string) bool { diff --git a/test/e2e/workflow_test.go b/test/e2e/workflow_test.go index cb646981c..6cbeee7eb 100644 --- a/test/e2e/workflow_test.go +++ b/test/e2e/workflow_test.go @@ -208,6 +208,8 @@ var _ = Describe("Validate the persistence ", Ordered, func() { }, 12*time.Minute).Should(BeTrue()) }, Entry("defined in the workflow from an existing kubernetes service as a reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("by_service")), + Entry("defined from the sonataflow platform as reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform")), + Entry("defined in the workflow and from the sonataflow platform", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_overwritten_by_service")), ) }) diff --git a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml index 3bca4cd73..248146a3f 100644 --- a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml +++ b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml @@ -29,12 +29,12 @@ spec: postgresql: secretRef: name: postgres-secrets - userKey: POSTGRES_USER - passwordKey: POSTGRES_PASSWORD + userKey: USER + passwordKey: PASSWORD serviceRef: - name: postgres + name: no-db-exists port: 5432 - databaseName: sonataflow + databaseName: find-me services: dataIndex: enabled: false diff --git a/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml index 4c1e0fb9d..0c60b56c3 100644 --- a/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml +++ b/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml @@ -28,7 +28,6 @@ spec: passwordKey: POSTGRES_PASSWORD serviceRef: name: postgres - namespace: default port: 5432 databaseName: sonataflow databaseSchema: callbackstatetimeouts diff --git a/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml index 34c40cbae..59faa58fd 100644 --- a/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml @@ -25,7 +25,6 @@ spec: passwordKey: POSTGRES_PASSWORD serviceRef: name: postgres - namespace: default port: 5432 databaseName: sonataflow build: diff --git a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/01-postgres.yaml b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/01-postgres.yaml new file mode 100644 index 000000000..662de4c7b --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/01-postgres.yaml @@ -0,0 +1,86 @@ +# 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. + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: postgres + template: + metadata: + labels: + app.kubernetes.io/name: postgres + spec: + containers: + - name: postgres + image: postgres:13.2-alpine + imagePullPolicy: 'IfNotPresent' + ports: + - containerPort: 5432 + volumeMounts: + - name: storage + mountPath: /var/lib/postgresql/data + envFrom: + - secretRef: + name: postgres-secrets + readinessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + livenessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + resources: + limits: + memory: "256Mi" + cpu: "500m" + volumes: + - name: storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + selector: + app.kubernetes.io/name: postgres + ports: + - port: 5432 diff --git a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml new file mode 100644 index 000000000..a7342ed12 --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml @@ -0,0 +1,37 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform +spec: + persistence: + postgresql: + secretRef: + name: my-secret + userKey: MY_USER + passwordKey: MY_PASSWORD + serviceRef: + name: db_not_defined + port: 3456 + databaseName: db_name + build: + template: + buildArgs: + - name: QUARKUS_EXTENSIONS + value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:999-SNAPSHOT,org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final + config: + strategyOptions: + KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/03-sonataflow_callbackstatetimeouts.sw.yaml new file mode 100644 index 000000000..bb64d2753 --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/03-sonataflow_callbackstatetimeouts.sw.yaml @@ -0,0 +1,101 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlow +metadata: + name: callbackstatetimeouts + annotations: + sonataflow.org/description: Callback State Timeouts Example k8s + sonataflow.org/version: 0.0.1 +spec: + persistence: + postgresql: + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: postgres + port: 5432 + databaseName: sonataflow + databaseSchema: callbackstatetimeouts + podTemplate: + container: + env: + - name: QUARKUS_FLYWAY_MIGRATE_AT_START + value: "true" + initContainers: + - name: init-postgres + 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;' ] + flow: + start: PrintStartMessage + events: + - name: callbackEvent + source: '' + type: callback_event_type + functions: + - name: systemOut + type: custom + operation: sysout + states: + - name: PrintStartMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has started.\"}" + transition: CallbackState + - name: CallbackState + type: callback + action: + name: callbackAction + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has executed the callbackFunction.\"}" + eventRef: callbackEvent + transition: CheckEventArrival + timeouts: + eventTimeout: PT30S + - name: CheckEventArrival + type: switch + dataConditions: + - condition: "${ .eventData != null }" + transition: EventArrived + defaultCondition: + transition: EventNotArrived + - name: EventArrived + type: inject + data: + exitMessage: "The callback event has arrived." + transition: PrintExitMessage + - name: EventNotArrived + type: inject + data: + exitMessage: "The callback event has not arrived, and the timeout has overdue." + transition: PrintExitMessage + - name: PrintExitMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has finalized. \" + .exitMessage + \" eventData: \" + .eventData}" + end: true diff --git a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/kustomization.yaml b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/kustomization.yaml new file mode 100644 index 000000000..b7f587bcc --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/kustomization.yaml @@ -0,0 +1,33 @@ +# 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. + +resources: +- 01-postgres.yaml +- 02-sonataflow_platform.yaml +- 03-sonataflow_callbackstatetimeouts.sw.yaml + +generatorOptions: + disableNameSuffixHash: true + +secretGenerator: + - name: postgres-secrets + literals: + - POSTGRES_USER=sonataflow + - POSTGRES_PASSWORD=sonataflow + - POSTGRES_DATABASE=sonataflow + - PGDATA=/var/lib/pgsql/data/userdata + +sortOptions: + order: fifo + diff --git a/test/yaml.go b/test/yaml.go index c769f4812..80aed8f5a 100644 --- a/test/yaml.go +++ b/test/yaml.go @@ -262,9 +262,6 @@ func GetSonataFlowE2eOrderProcessingFolder() string { func GetSonataFlowE2EPlatformServicesDirectory() string { return filepath.Join(getTestDataDir(), "platform", "services") } -func GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory(subdir string) string { - return filepath.Join(getTestDataDir(), "persistence", "workflow", subdir) -} func GetSonataFlowE2EPlatformPersistenceSampleDataDirectory(subdir string) string { return filepath.Join(getTestDataDir(), "platform", "persistence", subdir) From 11d312352a01837ffbb87c16c4c39aac3a59bb80 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 26 Jan 2024 13:37:44 -0500 Subject: [PATCH 06/22] Move image references in test to use ubi9/ubi-micro:latest Signed-off-by: Jordi Gil --- .../profiles/prod/profile_prod_test.go | 4 +- .../workflow/by_service/01-postgres.yaml | 86 --------------- .../by_service/02-sonataflow_platform.yaml | 27 ----- ...3-sonataflow_callbackstatetimeouts.sw.yaml | 102 ------------------ .../workflow/by_service/kustomization.yaml | 33 ------ .../02-sonataflow_platform.yaml | 4 +- .../02-sonataflow_platform.yaml | 4 +- .../postgreSQL/02-sonataflow_platform.yaml | 4 +- .../postgreSQL/02-sonataflow_platform.yaml | 4 +- ...3-sonataflow_callbackstatetimeouts.sw.yaml | 2 +- 10 files changed, 11 insertions(+), 259 deletions(-) delete mode 100644 test/testdata/persistence/workflow/by_service/01-postgres.yaml delete mode 100644 test/testdata/persistence/workflow/by_service/02-sonataflow_platform.yaml delete mode 100644 test/testdata/persistence/workflow/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml delete mode 100644 test/testdata/persistence/workflow/by_service/kustomization.yaml diff --git a/controllers/profiles/prod/profile_prod_test.go b/controllers/profiles/prod/profile_prod_test.go index 24a23a0bc..21c7ad03a 100644 --- a/controllers/profiles/prod/profile_prod_test.go +++ b/controllers/profiles/prod/profile_prod_test.go @@ -39,7 +39,7 @@ func Test_Reconciler_ProdOps(t *testing.T) { workflow := test.GetBaseSonataFlowWithProdOpsProfile(t.Name()) workflow.Spec.PodTemplate.PodSpec.InitContainers = append(workflow.Spec.PodTemplate.PodSpec.InitContainers, corev1.Container{ Name: "check-postgres", - Image: "registry.access.redhat.com/ubi9/ubi-minimal:latest", + Image: "registry.access.redhat.com/ubi9/ubi-micro:latest", Command: []string{"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;"}, }) client := test.NewSonataFlowClientBuilder(). @@ -74,7 +74,7 @@ func Test_Reconciler_ProdCustomPod(t *testing.T) { workflow := test.GetBaseSonataFlowWithProdProfile(t.Name()) workflow.Spec.PodTemplate.PodSpec.InitContainers = append(workflow.Spec.PodTemplate.PodSpec.InitContainers, corev1.Container{ Name: "check-postgres", - Image: "registry.access.redhat.com/ubi9/ubi-minimal:latest", + Image: "registry.access.redhat.com/ubi9/ubi-micro:latest", Command: []string{"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;"}, }) workflow.Status.Manager().MarkTrue(api.BuiltConditionType) diff --git a/test/testdata/persistence/workflow/by_service/01-postgres.yaml b/test/testdata/persistence/workflow/by_service/01-postgres.yaml deleted file mode 100644 index 662de4c7b..000000000 --- a/test/testdata/persistence/workflow/by_service/01-postgres.yaml +++ /dev/null @@ -1,86 +0,0 @@ -# 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. - ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - labels: - app.kubernetes.io/name: postgres - name: postgres-pvc -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/name: postgres - name: postgres -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: postgres - template: - metadata: - labels: - app.kubernetes.io/name: postgres - spec: - containers: - - name: postgres - image: postgres:13.2-alpine - imagePullPolicy: 'IfNotPresent' - ports: - - containerPort: 5432 - volumeMounts: - - name: storage - mountPath: /var/lib/postgresql/data - envFrom: - - secretRef: - name: postgres-secrets - readinessProbe: - exec: - command: ["pg_isready"] - initialDelaySeconds: 15 - timeoutSeconds: 2 - livenessProbe: - exec: - command: ["pg_isready"] - initialDelaySeconds: 15 - timeoutSeconds: 2 - resources: - limits: - memory: "256Mi" - cpu: "500m" - volumes: - - name: storage - persistentVolumeClaim: - claimName: postgres-pvc ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/name: postgres - name: postgres -spec: - selector: - app.kubernetes.io/name: postgres - ports: - - port: 5432 diff --git a/test/testdata/persistence/workflow/by_service/02-sonataflow_platform.yaml b/test/testdata/persistence/workflow/by_service/02-sonataflow_platform.yaml deleted file mode 100644 index 2a393718b..000000000 --- a/test/testdata/persistence/workflow/by_service/02-sonataflow_platform.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# 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. - -apiVersion: sonataflow.org/v1alpha08 -kind: SonataFlowPlatform -metadata: - name: sonataflow-platform -spec: - build: - template: - buildArgs: - - name: QUARKUS_EXTENSIONS - value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:999-SNAPSHOT,org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.10.Final,io.quarkus:quarkus-agroal:3.2.10.Final - config: - strategyOptions: - KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/persistence/workflow/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/persistence/workflow/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml deleted file mode 100644 index 9d0fcbc75..000000000 --- a/test/testdata/persistence/workflow/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml +++ /dev/null @@ -1,102 +0,0 @@ -# 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. - -apiVersion: sonataflow.org/v1alpha08 -kind: SonataFlow -metadata: - name: callbackstatetimeouts - annotations: - sonataflow.org/description: Callback State Timeouts Example k8s - sonataflow.org/version: 0.0.1 -spec: - persistence: - postgresql: - secretRef: - name: postgres-secrets - userKey: POSTGRES_USER - passwordKey: POSTGRES_PASSWORD - serviceRef: - name: postgres - port: 5432 - databaseName: sonataflow - databaseSchema: callbackstatetimeouts - podTemplate: - container: - env: - - name: QUARKUS_FLYWAY_MIGRATE_AT_START - value: "true" - initContainers: - - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-micro:latest - imagePullPolicy: IfNotPresent - #TODO: https://issues.redhat.com/browse/KOGITO-9840 - 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;' ] - flow: - start: PrintStartMessage - events: - - name: callbackEvent - source: '' - type: callback_event_type - functions: - - name: systemOut - type: custom - operation: sysout - states: - - name: PrintStartMessage - type: operation - actions: - - name: printSystemOut - functionRef: - refName: systemOut - arguments: - message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has started.\"}" - transition: CallbackState - - name: CallbackState - type: callback - action: - name: callbackAction - functionRef: - refName: systemOut - arguments: - message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has executed the callbackFunction.\"}" - eventRef: callbackEvent - transition: CheckEventArrival - timeouts: - eventTimeout: PT30S - - name: CheckEventArrival - type: switch - dataConditions: - - condition: "${ .eventData != null }" - transition: EventArrived - defaultCondition: - transition: EventNotArrived - - name: EventArrived - type: inject - data: - exitMessage: "The callback event has arrived." - transition: PrintExitMessage - - name: EventNotArrived - type: inject - data: - exitMessage: "The callback event has not arrived, and the timeout has overdue." - transition: PrintExitMessage - - name: PrintExitMessage - type: operation - actions: - - name: printSystemOut - functionRef: - refName: systemOut - arguments: - message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has finalized. \" + .exitMessage + \" eventData: \" + .eventData}" - end: true diff --git a/test/testdata/persistence/workflow/by_service/kustomization.yaml b/test/testdata/persistence/workflow/by_service/kustomization.yaml deleted file mode 100644 index b7f587bcc..000000000 --- a/test/testdata/persistence/workflow/by_service/kustomization.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# 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. - -resources: -- 01-postgres.yaml -- 02-sonataflow_platform.yaml -- 03-sonataflow_callbackstatetimeouts.sw.yaml - -generatorOptions: - disableNameSuffixHash: true - -secretGenerator: - - name: postgres-secrets - literals: - - POSTGRES_USER=sonataflow - - POSTGRES_PASSWORD=sonataflow - - POSTGRES_DATABASE=sonataflow - - PGDATA=/var/lib/pgsql/data/userdata - -sortOptions: - order: fifo - diff --git a/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml index 1dfbf4a7f..7f211ae80 100644 --- a/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml +++ b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml @@ -38,7 +38,7 @@ spec: podTemplate: initContainers: - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + 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;' ] jobService: @@ -46,6 +46,6 @@ spec: podTemplate: initContainers: - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + 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;' ] diff --git a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml index 248146a3f..ed8a9481f 100644 --- a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml +++ b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml @@ -48,7 +48,7 @@ spec: podTemplate: initContainers: - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + 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;' ] jobService: @@ -63,7 +63,7 @@ spec: podTemplate: initContainers: - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + 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;' ] diff --git a/test/testdata/platform/services/dev/postgreSQL/02-sonataflow_platform.yaml b/test/testdata/platform/services/dev/postgreSQL/02-sonataflow_platform.yaml index 15fd97df9..77d95c969 100644 --- a/test/testdata/platform/services/dev/postgreSQL/02-sonataflow_platform.yaml +++ b/test/testdata/platform/services/dev/postgreSQL/02-sonataflow_platform.yaml @@ -35,7 +35,7 @@ spec: podTemplate: initContainers: - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + 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;' ] jobService: @@ -50,6 +50,6 @@ spec: podTemplate: initContainers: - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + 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;' ] diff --git a/test/testdata/platform/services/prod/postgreSQL/02-sonataflow_platform.yaml b/test/testdata/platform/services/prod/postgreSQL/02-sonataflow_platform.yaml index 75acb6d7d..feae7a943 100644 --- a/test/testdata/platform/services/prod/postgreSQL/02-sonataflow_platform.yaml +++ b/test/testdata/platform/services/prod/postgreSQL/02-sonataflow_platform.yaml @@ -35,7 +35,7 @@ spec: podTemplate: initContainers: - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + 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;' ] jobService: @@ -50,6 +50,6 @@ spec: podTemplate: initContainers: - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + 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;' ] diff --git a/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml index 0c60b56c3..bb64d2753 100644 --- a/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml +++ b/test/testdata/workflow/persistence/by_service/03-sonataflow_callbackstatetimeouts.sw.yaml @@ -38,7 +38,7 @@ spec: value: "true" initContainers: - name: init-postgres - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + 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;' ] flow: From e49a12b9e3c87aa11d1158542b2773b75833a0b5 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 26 Jan 2024 14:14:42 -0500 Subject: [PATCH 07/22] Add descriptive message when unit test Test_Handler_WorkflowMinimalAndPropsAndSpecAndGeneric fails Signed-off-by: Jordi Gil --- workflowproj/workflowproj_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/workflowproj/workflowproj_test.go b/workflowproj/workflowproj_test.go index 9135e488d..0055518de 100644 --- a/workflowproj/workflowproj_test.go +++ b/workflowproj/workflowproj_test.go @@ -115,10 +115,8 @@ func Test_Handler_WorkflowMinimalAndPropsAndSpecAndGeneric(t *testing.T) { assert.Equal(t, "02-hello-resources", proj.Resources[1].Name) assert.Equal(t, proj.Workflow.Spec.Resources.ConfigMaps[0].ConfigMap.Name, proj.Resources[0].Name) assert.Equal(t, proj.Workflow.Spec.Resources.ConfigMaps[1].ConfigMap.Name, proj.Resources[1].Name) - assert.NotEmpty(t, proj.Resources[0].Data, fmt.Sprintf("Data in proj.Resources[0] is empty %+v", proj.Resources[0])) - assert.NotEmpty(t, proj.Resources[1].Data, fmt.Sprintf("Data in proj.Resources[1] is empty %+v", proj.Resources[1])) - assert.NotEmpty(t, proj.Resources[0].Data["myopenapi.json"]) - assert.NotEmpty(t, proj.Resources[1].Data["input.json"]) + assert.NotEmpty(t, proj.Resources[0].Data["myopenapi.json"], fmt.Sprintf("Data in proj.Resources[0] is empty %+v", proj.Resources[0])) + assert.NotEmpty(t, proj.Resources[1].Data["input.json"], fmt.Sprintf("Data in proj.Resources[1] is empty %+v", proj.Resources[1])) } func Test_Handler_WorklflowServiceAndPropsAndSpec_SaveAs(t *testing.T) { From f7d9b883bef0ae09ddb95f3e90e2581483ed9930 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 26 Jan 2024 16:18:55 -0500 Subject: [PATCH 08/22] Fix flaky test Test_Handler_WorkflowMinimalAndPropsAndSpecAndGeneric Signed-off-by: Jordi Gil --- workflowproj/workflowproj_test.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/workflowproj/workflowproj_test.go b/workflowproj/workflowproj_test.go index 0055518de..1c04c602a 100644 --- a/workflowproj/workflowproj_test.go +++ b/workflowproj/workflowproj_test.go @@ -32,6 +32,7 @@ import ( "github.com/apache/incubator-kie-kogito-serverless-operator/api/metadata" "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/scheme" ) @@ -115,8 +116,21 @@ func Test_Handler_WorkflowMinimalAndPropsAndSpecAndGeneric(t *testing.T) { assert.Equal(t, "02-hello-resources", proj.Resources[1].Name) assert.Equal(t, proj.Workflow.Spec.Resources.ConfigMaps[0].ConfigMap.Name, proj.Resources[0].Name) assert.Equal(t, proj.Workflow.Spec.Resources.ConfigMaps[1].ConfigMap.Name, proj.Resources[1].Name) - assert.NotEmpty(t, proj.Resources[0].Data["myopenapi.json"], fmt.Sprintf("Data in proj.Resources[0] is empty %+v", proj.Resources[0])) - assert.NotEmpty(t, proj.Resources[1].Data["input.json"], fmt.Sprintf("Data in proj.Resources[1] is empty %+v", proj.Resources[1])) + data, err := getResourceDataWithFileName(proj.Resources, "myopenapi.json") + assert.NoError(t, err) + assert.NotEmpty(t, data) + data, err = getResourceDataWithFileName(proj.Resources, "input.json") + assert.NoError(t, err) + assert.NotEmpty(t, data) +} + +func getResourceDataWithFileName(cms []*corev1.ConfigMap, fileName string) (string, error) { + for i := range cms { + if data, ok := cms[i].Data[fileName]; ok { + return data, nil + } + } + return "", fmt.Errorf("No configmap found with data containing filename %s", fileName) } func Test_Handler_WorklflowServiceAndPropsAndSpec_SaveAs(t *testing.T) { From 7eba3f2b836bfc611a9bb310b2d45bfaf5381c8b Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Mon, 29 Jan 2024 10:36:59 -0500 Subject: [PATCH 09/22] Collect cluster events and list all namespace/pods after running e2e tests Signed-off-by: Jordi Gil --- .github/workflows/e2e.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index cf3db3053..8a299bd8f 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -77,6 +77,12 @@ jobs: run: | make test-e2e + - name: Retrieve cluster events and list pods + if: failure() + run: | + kubectl get events + kubectl get pod -A + - name: Export kind logs if: always() run: | From 5655e61a8d16dcca7d32a9296f054bfd9994ac36 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Mon, 29 Jan 2024 14:58:05 -0500 Subject: [PATCH 10/22] Changes based on latest feedback from Ricardo Signed-off-by: Jordi Gil --- api/v1alpha08/sonataflowplatform_types.go | 20 ++++--------------- api/v1alpha08/zz_generated.deepcopy.go | 15 -------------- .../sonataflow.org_sonataflowplatforms.yaml | 3 ++- .../sonataflow.org_sonataflowplatforms.yaml | 3 ++- controllers/platform/services/services.go | 2 +- .../profiles/common/object_creators_test.go | 6 +++--- .../sonataflowplatform_controller_test.go | 4 ++-- operator.yaml | 3 ++- 8 files changed, 16 insertions(+), 40 deletions(-) diff --git a/api/v1alpha08/sonataflowplatform_types.go b/api/v1alpha08/sonataflowplatform_types.go index f71a88256..ed066aa5c 100644 --- a/api/v1alpha08/sonataflowplatform_types.go +++ b/api/v1alpha08/sonataflowplatform_types.go @@ -55,11 +55,9 @@ type SonataFlowPlatformSpec struct { } // PlatformPersistenceSpec configures the DataBase support for both platform services and workflows. For services, it allows -// configuring a generic database connectivity if the service does not come with its own configured. In case of the workflow -// the configuration is defined by the image name of the supported database. When the workflow is configured to require -// a database backend type supported by the platform (that is, the platform CR contains an image name for that database type) -// , the workflow pod is configured to contain a sidecar container running the database image defined here, and the workflow -// JDBC connectivity is setup to point to the DB container. +// configuring a generic database connectivity if the service does not come with its own configured. In case of workflows, +// the operator will add the necessary JDBC properties to in the workflow's application.properties so that it can communicate +// with the persistence service based on the spec provided here. // +optional type PlatformPersistenceSpec struct { // Connect configured services to a postgresql database. @@ -70,7 +68,7 @@ type PlatformPersistenceSpec struct { // PostgreSQLPlatformSpec provides the generic configuration details to configure the JDBC URL and establish a connection for each managed services when they don't provide their own configuration. type PostgreSQLPlatformSpec struct { // SecretRef contains the database user credentials - SecretRef SecretReference `json:"secretRef"` + SecretRef PostgreSqlSecretOptions `json:"secretRef"` // ServiceRef contains the K8s service name and namespace location of the PostgreSQL service. ServiceRef ServiceReference `json:"serviceRef,omitempty"` // Name of postgresql database to be used. Defaults to "sonataflow" @@ -90,16 +88,6 @@ type ServiceReference struct { Port int `json:"port,omitempty"` } -// SecretReference use of a secret to store the credentials to authenticate in the JDBC connection. -type SecretReference struct { - // Name of the postgresql credentials secret. This field is mandatory. - Name string `json:"name"` - // +optional - UserKey string `json:"userKey,omitempty"` - // +optional - PasswordKey string `json:"passwordKey,omitempty"` -} - // PlatformCluster is the kind of orchestration cluster the platform is installed into // +kubebuilder:validation:Enum=kubernetes;openshift type PlatformCluster string diff --git a/api/v1alpha08/zz_generated.deepcopy.go b/api/v1alpha08/zz_generated.deepcopy.go index 126a2b8fd..d6fafc77c 100644 --- a/api/v1alpha08/zz_generated.deepcopy.go +++ b/api/v1alpha08/zz_generated.deepcopy.go @@ -672,21 +672,6 @@ func (in *RegistrySpec) DeepCopy() *RegistrySpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SecretReference) DeepCopyInto(out *SecretReference) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretReference. -func (in *SecretReference) DeepCopy() *SecretReference { - if in == nil { - return nil - } - out := new(SecretReference) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceReference) DeepCopyInto(out *ServiceReference) { *out = *in diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml index 4834feb6f..c831626d5 100644 --- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml +++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml @@ -439,11 +439,12 @@ spec: properties: name: description: Name of the postgresql credentials secret. - This field is mandatory. type: string passwordKey: + description: Defaults to POSTGRESQL_PASSWORD type: string userKey: + description: Defaults to POSTGRESQL_USER type: string required: - name diff --git a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml index 86c1760d7..0c1a53772 100644 --- a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml +++ b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml @@ -440,11 +440,12 @@ spec: properties: name: description: Name of the postgresql credentials secret. - This field is mandatory. type: string passwordKey: + description: Defaults to POSTGRESQL_PASSWORD type: string userKey: + description: Defaults to POSTGRESQL_USER type: string required: - name diff --git a/controllers/platform/services/services.go b/controllers/platform/services/services.go index 81ce3cf35..bf05939c8 100644 --- a/controllers/platform/services/services.go +++ b/controllers/platform/services/services.go @@ -216,7 +216,7 @@ func (d DataIndexHandler) ConfigurePersistence(containerSpec *corev1.Container) c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnvFromPlatformSpec(d.platform.Spec.Persistence.PostgreSQL, d.GetServiceName())...) } // specific to DataIndex - c.Env = append(c.Env, corev1.EnvVar{Name: "QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION", Value: "update"}, corev1.EnvVar{Name: "QUARKUS_FLYWAY_MIGRATE_AT_START", Value: "true"}) + c.Env = append(c.Env, corev1.EnvVar{Name: quarkusHibernateORMDatabaseGeneration, Value: "update"}, corev1.EnvVar{Name: quarkusFlywayMigrateAtStart, Value: "true"}) return c } return containerSpec diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index 6720c6234..b31e897db 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -373,7 +373,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti &v1alpha08.PlatformPersistenceSpec{ PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ DatabaseName: "foo", - SecretRef: v1alpha08.SecretReference{ + SecretRef: v1alpha08.PostgreSqlSecretOptions{ Name: "foo_secret", UserKey: "username", PasswordKey: "password", @@ -444,7 +444,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t &v1alpha08.PlatformPersistenceSpec{ PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ DatabaseName: "foo", - SecretRef: v1alpha08.SecretReference{ + SecretRef: v1alpha08.PostgreSqlSecretOptions{ Name: "foo_secret", UserKey: "username", PasswordKey: "password", @@ -542,7 +542,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Req &v1alpha08.PlatformPersistenceSpec{ PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ DatabaseName: "foo", - SecretRef: v1alpha08.SecretReference{ + SecretRef: v1alpha08.PostgreSqlSecretOptions{ Name: "foo_secret", UserKey: "username", PasswordKey: "password", diff --git a/controllers/sonataflowplatform_controller_test.go b/controllers/sonataflowplatform_controller_test.go index 711d37fa9..9cdd79ae6 100644 --- a/controllers/sonataflowplatform_controller_test.go +++ b/controllers/sonataflowplatform_controller_test.go @@ -262,7 +262,7 @@ func TestSonataFlowPlatformController(t *testing.T) { }, Persistence: &v1alpha08.PlatformPersistenceSpec{ PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ - SecretRef: v1alpha08.SecretReference{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, + SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, ServiceRef: v1alpha08.ServiceReference{Name: "postgresql", Namespace: "default", Port: 5432}, DatabaseName: "sonataflow", }, @@ -367,7 +367,7 @@ func TestSonataFlowPlatformController(t *testing.T) { }, Persistence: &v1alpha08.PlatformPersistenceSpec{ PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ - SecretRef: v1alpha08.SecretReference{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, + SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, ServiceRef: v1alpha08.ServiceReference{Name: "postgresql", Namespace: "default", Port: 5432}, DatabaseName: "sonataflow", }, diff --git a/operator.yaml b/operator.yaml index 903a7b77b..ec1223542 100644 --- a/operator.yaml +++ b/operator.yaml @@ -918,11 +918,12 @@ spec: properties: name: description: Name of the postgresql credentials secret. - This field is mandatory. type: string passwordKey: + description: Defaults to POSTGRESQL_PASSWORD type: string userKey: + description: Defaults to POSTGRESQL_USER type: string required: - name From 69d0793b04d1c94eceda3d467a6f49e37438c9f3 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Tue, 30 Jan 2024 09:24:30 -0500 Subject: [PATCH 11/22] Remove duplicated tests Signed-off-by: Jordi Gil --- test/e2e/platform_test.go | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/test/e2e/platform_test.go b/test/e2e/platform_test.go index 125dd01bd..90c485d75 100644 --- a/test/e2e/platform_test.go +++ b/test/e2e/platform_test.go @@ -154,36 +154,4 @@ var _ = Describe("Validate the persistence", Ordered, func() { Entry("and both Job Service and Data Index using the one defined in each service, discarding the one from the platform CR", test.GetSonataFlowE2EPlatformPersistenceSampleDataDirectory("overwritten_by_services")), ) - DescribeTable("when deploying a SonataFlow CR with PostgreSQL persistence", func(testcaseDir string) { - By("Deploy the CR") - var manifests []byte - EventuallyWithOffset(1, func() error { - var err error - cmd := exec.Command("kubectl", "kustomize", testcaseDir) - manifests, err = utils.Run(cmd) - return err - }, time.Minute, time.Second).Should(Succeed()) - cmd := exec.Command("kubectl", "create", "-n", targetNamespace, "-f", "-") - cmd.Stdin = bytes.NewBuffer(manifests) - _, err := utils.Run(cmd) - Expect(err).NotTo(HaveOccurred()) - By("Wait for SonatatFlow CR to complete deployment") - // wait for service deployments to be ready - EventuallyWithOffset(1, func() error { - cmd = exec.Command("kubectl", "wait", "pod", "-n", targetNamespace, "-l", "sonataflow.org/workflow-app=callbackstatetimeouts", "--for", "condition=Ready", "--timeout=5s") - _, err = utils.Run(cmd) - return err - }, 10*time.Minute, 5).Should(Succeed()) - By("Evaluate status of the workflow's pod health endpoint") - cmd = exec.Command("kubectl", "get", "pod", "-l", "sonataflow.org/workflow-app=callbackstatetimeouts", "-n", targetNamespace, "-ojsonpath={.items[*].metadata.name}") - output, err := utils.Run(cmd) - Expect(err).NotTo(HaveOccurred()) - for _, pn := range strings.Split(string(output), " ") { - verifyDatabaseConnectionsHealthStatusInPod(pn, targetNamespace) - } - }, - Entry("defined in the workflow from an existing kubernetes service as a reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("by_service")), - Entry("defined in the workflow derived from the workflow platform CR", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform")), - ) - }) From 71799ffeadcee55996c29ccaca300a33086cdef4 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Wed, 31 Jan 2024 14:15:08 -0500 Subject: [PATCH 12/22] Aligned Persistence with Services, including adding support for JDBC URL string and dataSchema in the configuration to be inherited by the workflows. Signed-off-by: Jordi Gil --- .../sonataflowplatform_services_types.go | 18 ++--- api/v1alpha08/sonataflowplatform_types.go | 25 +------ api/v1alpha08/zz_generated.deepcopy.go | 68 +++++-------------- .../sonataflow.org_sonataflowplatforms.yaml | 35 ++++++---- .../sonataflow.org_sonataflowplatforms.yaml | 35 ++++++---- controllers/platform/services/properties.go | 2 +- .../services/properties_services_test.go | 12 ++-- .../platform/services/properties_test.go | 30 ++++---- controllers/platform/services/services.go | 29 ++++---- .../profiles/common/object_creators.go | 15 ++-- .../profiles/common/object_creators_test.go | 64 ++++++++--------- .../profiles/common/persistence/postgresql.go | 59 +--------------- .../common/properties/application_test.go | 6 +- .../sonataflowplatform_controller_test.go | 45 ++++++------ operator.yaml | 35 ++++++---- test/e2e/helpers.go | 24 ------- .../02-sonataflow_platform.yaml | 2 +- .../02-sonataflow_platform.yaml | 2 +- .../from_platform/02-sonataflow_platform.yaml | 3 +- .../02-sonataflow_platform.yaml | 3 +- 20 files changed, 204 insertions(+), 308 deletions(-) diff --git a/api/v1alpha08/sonataflowplatform_services_types.go b/api/v1alpha08/sonataflowplatform_services_types.go index 9ecad3de9..80260f853 100644 --- a/api/v1alpha08/sonataflowplatform_services_types.go +++ b/api/v1alpha08/sonataflowplatform_services_types.go @@ -43,26 +43,26 @@ type ServiceSpec struct { type PersistenceOptions struct { // Connect configured services to a postgresql database. // +optional - PostgreSql *PersistencePostgreSql `json:"postgresql,omitempty"` + PostgreSQL *PersistencePostgreSQL `json:"postgresql,omitempty"` } -// PersistencePostgreSql configure postgresql connection for service(s). +// PersistencePostgreSQL configure postgresql connection for service(s). // +kubebuilder:validation:MinProperties=2 // +kubebuilder:validation:MaxProperties=2 -type PersistencePostgreSql struct { +type PersistencePostgreSQL struct { // Secret reference to the database user credentials - SecretRef PostgreSqlSecretOptions `json:"secretRef"` + SecretRef PostgreSQLSecretOptions `json:"secretRef"` // Service reference to postgresql datasource. Mutually exclusive to jdbcUrl. // +optional - ServiceRef *PostgreSqlServiceOptions `json:"serviceRef,omitempty"` + ServiceRef *PostgreSQLServiceOptions `json:"serviceRef,omitempty"` // PostgreSql JDBC URL. Mutually exclusive to serviceRef. // e.g. "jdbc:postgresql://host:port/database?currentSchema=data-index-service" // +optional JdbcUrl string `json:"jdbcUrl,omitempty"` } -// PostgreSqlSecretOptions use credential secret for postgresql connection. -type PostgreSqlSecretOptions struct { +// PostgreSQLSecretOptions use credential secret for postgresql connection. +type PostgreSQLSecretOptions struct { // Name of the postgresql credentials secret. Name string `json:"name"` // Defaults to POSTGRESQL_USER @@ -73,8 +73,8 @@ type PostgreSqlSecretOptions struct { PasswordKey string `json:"passwordKey,omitempty"` } -// PostgreSqlServiceOptions use k8s service to configure postgresql jdbc url. -type PostgreSqlServiceOptions struct { +// PostgreSQLServiceOptions use k8s service to configure postgresql jdbc url. +type PostgreSQLServiceOptions struct { // Name of the postgresql k8s service. Name string `json:"name"` // Namespace of the postgresql k8s service. Defaults to the SonataFlowPlatform's local namespace. diff --git a/api/v1alpha08/sonataflowplatform_types.go b/api/v1alpha08/sonataflowplatform_types.go index ed066aa5c..81b6279a1 100644 --- a/api/v1alpha08/sonataflowplatform_types.go +++ b/api/v1alpha08/sonataflowplatform_types.go @@ -62,30 +62,7 @@ type SonataFlowPlatformSpec struct { type PlatformPersistenceSpec struct { // Connect configured services to a postgresql database. // +optional - PostgreSQL *PostgreSQLPlatformSpec `json:"postgresql,omitempty"` -} - -// PostgreSQLPlatformSpec provides the generic configuration details to configure the JDBC URL and establish a connection for each managed services when they don't provide their own configuration. -type PostgreSQLPlatformSpec struct { - // SecretRef contains the database user credentials - SecretRef PostgreSqlSecretOptions `json:"secretRef"` - // ServiceRef contains the K8s service name and namespace location of the PostgreSQL service. - ServiceRef ServiceReference `json:"serviceRef,omitempty"` - // Name of postgresql database to be used. Defaults to "sonataflow" - // +kubebuilder:default:=sonataflow - DatabaseName string `json:"databaseName,omitempty"` -} - -type ServiceReference struct { - // Name contains the name of the kubernetes service. This field is mandatory. - // +required - Name string `json:"name"` - // Namespace contains the name of the namespace where the kubernetes service resides. This field is optional. - // +optional - Namespace string `json:"namespace"` - // Port contains the port number associated to the kubernetes service. This field is mandatory. - // +required - Port int `json:"port,omitempty"` + PostgreSQL *PersistencePostgreSQL `json:"postgresql,omitempty"` } // PlatformCluster is the kind of orchestration cluster the platform is installed into diff --git a/api/v1alpha08/zz_generated.deepcopy.go b/api/v1alpha08/zz_generated.deepcopy.go index d6fafc77c..ed0358e73 100644 --- a/api/v1alpha08/zz_generated.deepcopy.go +++ b/api/v1alpha08/zz_generated.deepcopy.go @@ -322,9 +322,9 @@ func (in *Flow) DeepCopy() *Flow { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PersistenceOptions) DeepCopyInto(out *PersistenceOptions) { *out = *in - if in.PostgreSql != nil { - in, out := &in.PostgreSql, &out.PostgreSql - *out = new(PersistencePostgreSql) + if in.PostgreSQL != nil { + in, out := &in.PostgreSQL, &out.PostgreSQL + *out = new(PersistencePostgreSQL) (*in).DeepCopyInto(*out) } } @@ -340,22 +340,22 @@ func (in *PersistenceOptions) DeepCopy() *PersistenceOptions { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PersistencePostgreSql) DeepCopyInto(out *PersistencePostgreSql) { +func (in *PersistencePostgreSQL) DeepCopyInto(out *PersistencePostgreSQL) { *out = *in out.SecretRef = in.SecretRef if in.ServiceRef != nil { in, out := &in.ServiceRef, &out.ServiceRef - *out = new(PostgreSqlServiceOptions) + *out = new(PostgreSQLServiceOptions) (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistencePostgreSql. -func (in *PersistencePostgreSql) DeepCopy() *PersistencePostgreSql { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistencePostgreSQL. +func (in *PersistencePostgreSQL) DeepCopy() *PersistencePostgreSQL { if in == nil { return nil } - out := new(PersistencePostgreSql) + out := new(PersistencePostgreSQL) in.DeepCopyInto(out) return out } @@ -365,8 +365,8 @@ func (in *PlatformPersistenceSpec) DeepCopyInto(out *PlatformPersistenceSpec) { *out = *in if in.PostgreSQL != nil { in, out := &in.PostgreSQL, &out.PostgreSQL - *out = new(PostgreSQLPlatformSpec) - **out = **in + *out = new(PersistencePostgreSQL) + (*in).DeepCopyInto(*out) } } @@ -606,39 +606,22 @@ func (in *PodTemplateSpec) DeepCopy() *PodTemplateSpec { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PostgreSQLPlatformSpec) DeepCopyInto(out *PostgreSQLPlatformSpec) { +func (in *PostgreSQLSecretOptions) DeepCopyInto(out *PostgreSQLSecretOptions) { *out = *in - out.SecretRef = in.SecretRef - out.ServiceRef = in.ServiceRef } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLPlatformSpec. -func (in *PostgreSQLPlatformSpec) DeepCopy() *PostgreSQLPlatformSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLSecretOptions. +func (in *PostgreSQLSecretOptions) DeepCopy() *PostgreSQLSecretOptions { if in == nil { return nil } - out := new(PostgreSQLPlatformSpec) + out := new(PostgreSQLSecretOptions) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PostgreSqlSecretOptions) DeepCopyInto(out *PostgreSqlSecretOptions) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSqlSecretOptions. -func (in *PostgreSqlSecretOptions) DeepCopy() *PostgreSqlSecretOptions { - if in == nil { - return nil - } - out := new(PostgreSqlSecretOptions) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PostgreSqlServiceOptions) DeepCopyInto(out *PostgreSqlServiceOptions) { +func (in *PostgreSQLServiceOptions) DeepCopyInto(out *PostgreSQLServiceOptions) { *out = *in if in.Port != nil { in, out := &in.Port, &out.Port @@ -647,12 +630,12 @@ func (in *PostgreSqlServiceOptions) DeepCopyInto(out *PostgreSqlServiceOptions) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSqlServiceOptions. -func (in *PostgreSqlServiceOptions) DeepCopy() *PostgreSqlServiceOptions { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLServiceOptions. +func (in *PostgreSQLServiceOptions) DeepCopy() *PostgreSQLServiceOptions { if in == nil { return nil } - out := new(PostgreSqlServiceOptions) + out := new(PostgreSQLServiceOptions) in.DeepCopyInto(out) return out } @@ -672,21 +655,6 @@ func (in *RegistrySpec) DeepCopy() *RegistrySpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceReference) DeepCopyInto(out *ServiceReference) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceReference. -func (in *ServiceReference) DeepCopy() *ServiceReference { - if in == nil { - return nil - } - out := new(ServiceReference) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { *out = *in diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml index c831626d5..39b444fcd 100644 --- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml +++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml @@ -428,14 +428,15 @@ spec: properties: postgresql: description: Connect configured services to a postgresql database. + maxProperties: 2 + minProperties: 2 properties: - databaseName: - default: sonataflow - description: Name of postgresql database to be used. Defaults - to "sonataflow" + jdbcUrl: + description: PostgreSql JDBC URL. Mutually exclusive to serviceRef. + e.g. "jdbc:postgresql://host:port/database?currentSchema=data-index-service" type: string secretRef: - description: SecretRef contains the database user credentials + description: Secret reference to the database user credentials properties: name: description: Name of the postgresql credentials secret. @@ -450,21 +451,27 @@ spec: - name type: object serviceRef: - description: ServiceRef contains the K8s service name and - namespace location of the PostgreSQL service. + 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 contains the name of the kubernetes - service. This field is mandatory. + description: Name of the postgresql k8s service. type: string namespace: - description: Namespace contains the name of the namespace - where the kubernetes service resides. This field is - optional. + description: Namespace of the postgresql k8s service. + Defaults to the SonataFlowPlatform's local namespace. type: string port: - description: Port contains the port number associated - to the kubernetes service. This field is mandatory. + description: Port to use when connecting to the postgresql + k8s service. Defaults to 5432. type: integer required: - name diff --git a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml index 0c1a53772..5823fdaf2 100644 --- a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml +++ b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml @@ -429,14 +429,15 @@ spec: properties: postgresql: description: Connect configured services to a postgresql database. + maxProperties: 2 + minProperties: 2 properties: - databaseName: - default: sonataflow - description: Name of postgresql database to be used. Defaults - to "sonataflow" + jdbcUrl: + description: PostgreSql JDBC URL. Mutually exclusive to serviceRef. + e.g. "jdbc:postgresql://host:port/database?currentSchema=data-index-service" type: string secretRef: - description: SecretRef contains the database user credentials + description: Secret reference to the database user credentials properties: name: description: Name of the postgresql credentials secret. @@ -451,21 +452,27 @@ spec: - name type: object serviceRef: - description: ServiceRef contains the K8s service name and - namespace location of the PostgreSQL service. + 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 contains the name of the kubernetes - service. This field is mandatory. + description: Name of the postgresql k8s service. type: string namespace: - description: Namespace contains the name of the namespace - where the kubernetes service resides. This field is - optional. + description: Namespace of the postgresql k8s service. + Defaults to the SonataFlowPlatform's local namespace. type: string port: - description: Port contains the port number associated - to the kubernetes service. This field is mandatory. + description: Port to use when connecting to the postgresql + k8s service. Defaults to 5432. type: integer required: - name diff --git a/controllers/platform/services/properties.go b/controllers/platform/services/properties.go index a29a7f503..02fc65002 100644 --- a/controllers/platform/services/properties.go +++ b/controllers/platform/services/properties.go @@ -96,7 +96,7 @@ func (a *serviceAppPropertyHandler) Build() string { return props.String() } -func generateReactiveURL(postgresSpec *operatorapi.PersistencePostgreSql, schema string, namespace string, dbName string, port int) (string, error) { +func generateReactiveURL(postgresSpec *operatorapi.PersistencePostgreSQL, schema string, namespace string, dbName string, port int) (string, error) { if len(postgresSpec.JdbcUrl) > 0 { s := strings.TrimLeft(postgresSpec.JdbcUrl, "jdbc:") u, err := url.Parse(s) diff --git a/controllers/platform/services/properties_services_test.go b/controllers/platform/services/properties_services_test.go index 90da80837..08dfe6d00 100644 --- a/controllers/platform/services/properties_services_test.go +++ b/controllers/platform/services/properties_services_test.go @@ -225,10 +225,10 @@ func setJobServiceJDBC(jdbc string) plfmOptionFn { if p.Spec.Services.JobService.Persistence == nil { p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceOptions{} } - if p.Spec.Services.JobService.Persistence.PostgreSql == nil { - p.Spec.Services.JobService.Persistence.PostgreSql = &operatorapi.PersistencePostgreSql{} + if p.Spec.Services.JobService.Persistence.PostgreSQL == nil { + p.Spec.Services.JobService.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} } - p.Spec.Services.JobService.Persistence.PostgreSql.JdbcUrl = jdbc + p.Spec.Services.JobService.Persistence.PostgreSQL.JdbcUrl = jdbc } } @@ -240,9 +240,9 @@ func setDataIndexJDBC(jdbc string) plfmOptionFn { if p.Spec.Services.DataIndex.Persistence == nil { p.Spec.Services.DataIndex.Persistence = &operatorapi.PersistenceOptions{} } - if p.Spec.Services.DataIndex.Persistence.PostgreSql == nil { - p.Spec.Services.DataIndex.Persistence.PostgreSql = &operatorapi.PersistencePostgreSql{} + if p.Spec.Services.DataIndex.Persistence.PostgreSQL == nil { + p.Spec.Services.DataIndex.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} } - p.Spec.Services.DataIndex.Persistence.PostgreSql.JdbcUrl = jdbc + p.Spec.Services.DataIndex.Persistence.PostgreSQL.JdbcUrl = jdbc } } diff --git a/controllers/platform/services/properties_test.go b/controllers/platform/services/properties_test.go index 7f37b1923..7697cb985 100644 --- a/controllers/platform/services/properties_test.go +++ b/controllers/platform/services/properties_test.go @@ -34,7 +34,7 @@ const ( var _ = Describe("Platform properties", func() { var _ = Context("PostgreSQL properties", func() { - var _ = DescribeTable("Generate a reactive URL", func(spec *operatorapi.PersistencePostgreSql, expectedReactiveURL string, expectedError bool) { + var _ = DescribeTable("Generate a reactive URL", func(spec *operatorapi.PersistencePostgreSQL, expectedReactiveURL string, expectedError bool) { res, err := generateReactiveURL(spec, defaultSchema, "default", constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) if expectedError { Expect(err).NotTo(BeNil()) @@ -72,10 +72,10 @@ var _ = Describe("Platform properties", func() { }) }) -type optionFn func(*operatorapi.PersistencePostgreSql) +type optionFn func(*operatorapi.PersistencePostgreSQL) -func generatePostgreSQLOptions(options ...optionFn) *operatorapi.PersistencePostgreSql { - p := &operatorapi.PersistencePostgreSql{} +func generatePostgreSQLOptions(options ...optionFn) *operatorapi.PersistencePostgreSQL { + p := &operatorapi.PersistencePostgreSQL{} for _, f := range options { f(p) } @@ -83,51 +83,51 @@ func generatePostgreSQLOptions(options ...optionFn) *operatorapi.PersistencePost } func setJDBC(url string) optionFn { - return func(o *operatorapi.PersistencePostgreSql) { + return func(o *operatorapi.PersistencePostgreSQL) { o.JdbcUrl = url } } func setServiceName(svcName string) optionFn { - return func(o *operatorapi.PersistencePostgreSql) { + return func(o *operatorapi.PersistencePostgreSQL) { if o.ServiceRef == nil { - o.ServiceRef = &operatorapi.PostgreSqlServiceOptions{} + o.ServiceRef = &operatorapi.PostgreSQLServiceOptions{} } o.ServiceRef.Name = svcName } } func setDatabaseSchemaName(dbSchemaName string) optionFn { - return func(o *operatorapi.PersistencePostgreSql) { + return func(o *operatorapi.PersistencePostgreSQL) { if o.ServiceRef == nil { - o.ServiceRef = &operatorapi.PostgreSqlServiceOptions{} + o.ServiceRef = &operatorapi.PostgreSQLServiceOptions{} } o.ServiceRef.DatabaseSchema = dbSchemaName } } func setDatabaseName(dbName string) optionFn { - return func(o *operatorapi.PersistencePostgreSql) { + return func(o *operatorapi.PersistencePostgreSQL) { if o.ServiceRef == nil { - o.ServiceRef = &operatorapi.PostgreSqlServiceOptions{} + o.ServiceRef = &operatorapi.PostgreSQLServiceOptions{} } o.ServiceRef.DatabaseName = dbName } } func setServiceNamespace(svcNamespace string) optionFn { - return func(o *operatorapi.PersistencePostgreSql) { + return func(o *operatorapi.PersistencePostgreSQL) { if o.ServiceRef == nil { - o.ServiceRef = &operatorapi.PostgreSqlServiceOptions{} + o.ServiceRef = &operatorapi.PostgreSQLServiceOptions{} } o.ServiceRef.Namespace = svcNamespace } } func setDBPort(portNumber int) optionFn { - return func(o *operatorapi.PersistencePostgreSql) { + return func(o *operatorapi.PersistencePostgreSQL) { if o.ServiceRef == nil { - o.ServiceRef = &operatorapi.PostgreSqlServiceOptions{} + o.ServiceRef = &operatorapi.PostgreSQLServiceOptions{} } o.ServiceRef.Port = &portNumber } diff --git a/controllers/platform/services/services.go b/controllers/platform/services/services.go index bf05939c8..4ce41687d 100644 --- a/controllers/platform/services/services.go +++ b/controllers/platform/services/services.go @@ -200,21 +200,22 @@ func (d DataIndexHandler) MergePodSpec(podSpec corev1.PodSpec) (corev1.PodSpec, // hasPostgreSQLConfigured returns true when either the SonataFlow Platform PostgreSQL CR's structure or the one in the Data Index service specification is not nil func (d DataIndexHandler) hasPostgreSQLConfigured() bool { - return (d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSql != nil) || + return (d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSQL != nil) || (d.platform.Spec.Persistence != nil && d.platform.Spec.Persistence.PostgreSQL != nil) } func (d DataIndexHandler) ConfigurePersistence(containerSpec *corev1.Container) *corev1.Container { if d.hasPostgreSQLConfigured() { - p := d.platform.Spec.Services.DataIndex.Persistence + var p *operatorapi.PersistencePostgreSQL c := containerSpec.DeepCopy() c.Image = d.GetServiceImageName(constants.PersistenceTypePostgreSQL) - if d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSql != nil { - c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p.PostgreSql, d.GetServiceName(), d.platform.Namespace)...) + if d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSQL != nil { + p = d.platform.Spec.Services.DataIndex.Persistence.PostgreSQL } else { - c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnvFromPlatformSpec(d.platform.Spec.Persistence.PostgreSQL, d.GetServiceName())...) + p = d.platform.Spec.Persistence.PostgreSQL } + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p, 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 @@ -372,20 +373,22 @@ func (j JobServiceHandler) MergeContainerSpec(containerSpec *corev1.Container) ( // hasPostgreSQLConfigured returns true when either the SonataFlow Platform PostgreSQL CR's structure or the one in the Job service specification is not nil func (j JobServiceHandler) hasPostgreSQLConfigured() bool { - return (j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSql != nil) || + return (j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSQL != nil) || (j.platform.Spec.Persistence != nil && j.platform.Spec.Persistence.PostgreSQL != nil) } func (j JobServiceHandler) ConfigurePersistence(containerSpec *corev1.Container) *corev1.Container { if j.hasPostgreSQLConfigured() { + var p *operatorapi.PersistencePostgreSQL c := containerSpec.DeepCopy() c.Image = j.GetServiceImageName(constants.PersistenceTypePostgreSQL) - if j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSql != nil { - c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(j.platform.Spec.Services.JobService.Persistence.PostgreSql, j.GetServiceName(), j.platform.Namespace)...) + if j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSQL != nil { + p = j.platform.Spec.Services.JobService.Persistence.PostgreSQL } else { - c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnvFromPlatformSpec(j.platform.Spec.Persistence.PostgreSQL, j.GetServiceName())...) + p = j.platform.Spec.Persistence.PostgreSQL } + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p, j.GetServiceName(), j.platform.Namespace)...) // Specific to Job Service c.Env = append(c.Env, corev1.EnvVar{Name: "QUARKUS_FLYWAY_MIGRATE_AT_START", Value: "true"}) return c @@ -408,8 +411,8 @@ func (j JobServiceHandler) GenerateServiceProperties() (*properties.Properties, var dataSourceReactiveURL string var err error jspec := j.platform.Spec.Services.JobService - if j.IsServiceSetInSpec() && jspec.Persistence != nil && jspec.Persistence.PostgreSql != nil { - dataSourceReactiveURL, err = generateReactiveURL(j.platform.Spec.Services.JobService.Persistence.PostgreSql, j.GetServiceName(), j.platform.Namespace, constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) + if j.IsServiceSetInSpec() && jspec.Persistence != nil && jspec.Persistence.PostgreSQL != nil { + dataSourceReactiveURL, err = generateReactiveURL(j.platform.Spec.Services.JobService.Persistence.PostgreSQL, j.GetServiceName(), j.platform.Namespace, constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) if err != nil { return nil, err } @@ -419,7 +422,7 @@ func (j JobServiceHandler) GenerateServiceProperties() (*properties.Properties, if len(p.ServiceRef.Namespace) > 0 { namespace = fmt.Sprintf(".%s", p.ServiceRef.Namespace) } - dataSourceReactiveURL = fmt.Sprintf("%s://%s%s:%d/%s?search_path=%s", constants.PersistenceTypePostgreSQL, p.ServiceRef.Name, namespace, p.ServiceRef.Port, p.DatabaseName, j.GetServiceName()) + dataSourceReactiveURL = fmt.Sprintf("%s://%s%s:%d/%s?search_path=%s", constants.PersistenceTypePostgreSQL, p.ServiceRef.Name, namespace, *p.ServiceRef.Port, p.ServiceRef.DatabaseName, j.GetServiceName()) } props.Set(constants.JobServiceDataSourceReactiveURL, dataSourceReactiveURL) } @@ -461,7 +464,7 @@ func isJobServiceSet(platform *operatorapi.SonataFlowPlatform) bool { } func isServicesSet(platform *operatorapi.SonataFlowPlatform) bool { - return platform != nil && platform.Spec.Services != nil + return platform != nil && (platform.Spec.Services.JobService != nil || platform.Spec.Services.DataIndex != nil) } func generateServiceURL(protocol string, namespace string, name string) string { diff --git a/controllers/profiles/common/object_creators.go b/controllers/profiles/common/object_creators.go index 33bff76f0..98f1e9661 100644 --- a/controllers/profiles/common/object_creators.go +++ b/controllers/profiles/common/object_creators.go @@ -241,14 +241,15 @@ func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorap } c := serviceContainer.DeepCopy() - if config.PostgreSql != nil { - c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(config.PostgreSql, defaultSchema, namespace)...) - return c, nil + var p *operatorapi.PersistencePostgreSQL + if config.PostgreSQL == nil && persistence.WorkflowConfig.GetPostgreSQLConfiguration() == nil { + return nil, fmt.Errorf("no persistence specification available in the workflow or in the platform") } - p := persistence.WorkflowConfig.GetPostgreSQLConfiguration() - if p == nil { - return nil, fmt.Errorf("platform persistence configuration is undefined") + if config.PostgreSQL != nil { + p = config.PostgreSQL + } else if persistence.WorkflowConfig.GetPostgreSQLConfiguration() != nil { + p = persistence.WorkflowConfig.GetPostgreSQLConfiguration() } - c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnvFromPlatformSpec(p, defaultSchema)...) + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p, defaultSchema, namespace)...) return c, nil } diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index b31e897db..1ae39eaec 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -215,8 +215,8 @@ func TestMergePodSpec_WithPostgreSQL_and_JDBC_URL_field(t *testing.T) { }, }, Persistence: &v1alpha08.PersistenceOptions{ - PostgreSql: &v1alpha08.PersistencePostgreSql{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "test"}, + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: "jdbc:postgresql://host:port/database?currentSchema=workflow", }, }, @@ -305,9 +305,9 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_In_Workflow_CR(t *testin }, }, Persistence: &v1alpha08.PersistenceOptions{ - PostgreSql: &v1alpha08.PersistencePostgreSql{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "test"}, - ServiceRef: &v1alpha08.PostgreSqlServiceOptions{ + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ Name: "test", Namespace: "foo", Port: &postgreSQLPort, @@ -371,17 +371,18 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_In_Workflow_CR(t *testin func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesting_It(t *testing.T) { persistence.WorkflowConfig.SetConfig( &v1alpha08.PlatformPersistenceSpec{ - PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ - DatabaseName: "foo", - SecretRef: v1alpha08.PostgreSqlSecretOptions{ + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", UserKey: "username", PasswordKey: "password", }, - ServiceRef: v1alpha08.ServiceReference{ - Name: "service_name", - Namespace: "service_namespace", - Port: 5432, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ + Name: "service_name", + Namespace: "service_namespace", + Port: &postgreSQLPort, + DatabaseName: "foo", + DatabaseSchema: "bar", }, }}) workflow := test.GetBaseSonataFlow(t.Name()) @@ -417,7 +418,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti }, { Name: "QUARKUS_DATASOURCE_JDBC_URL", - Value: "jdbc:postgresql://service_name.service_namespace:5432/foo?currentSchema=greeting", + Value: "jdbc:postgresql://service_name.service_namespace:5432/foo?currentSchema=bar", }, { Name: "KOGITO_PERSISTENCE_TYPE", @@ -442,17 +443,18 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *testing.T) { persistence.WorkflowConfig.SetConfig( &v1alpha08.PlatformPersistenceSpec{ - PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ - DatabaseName: "foo", - SecretRef: v1alpha08.PostgreSqlSecretOptions{ + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", UserKey: "username", PasswordKey: "password", }, - ServiceRef: v1alpha08.ServiceReference{ - Name: "service_name", - Namespace: "service_namespace", - Port: 5432, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ + Name: "service_name", + Namespace: "service_namespace", + Port: &postgreSQLPort, + DatabaseName: "foo", + DatabaseSchema: "bar", }, }}) workflow := test.GetBaseSonataFlow(t.Name()) @@ -475,9 +477,9 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t }, }, Persistence: &v1alpha08.PersistenceOptions{ - PostgreSql: &v1alpha08.PersistencePostgreSql{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "test"}, - ServiceRef: &v1alpha08.PostgreSqlServiceOptions{ + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ Name: "test", Namespace: "default", Port: &postgreSQLPort, @@ -540,17 +542,17 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Requesting_it(t *testing.T) { persistence.WorkflowConfig.SetConfig( &v1alpha08.PlatformPersistenceSpec{ - PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ - DatabaseName: "foo", - SecretRef: v1alpha08.PostgreSqlSecretOptions{ + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", UserKey: "username", PasswordKey: "password", }, - ServiceRef: v1alpha08.ServiceReference{ - Name: "service_name", - Namespace: "service_namespace", - Port: 5432, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ + Name: "service_name", + Namespace: "service_namespace", + Port: &postgreSQLPort, + DatabaseName: "foo", }, }}) workflow := test.GetBaseSonataFlow(t.Name()) @@ -576,5 +578,5 @@ func TestMergePodSpec_WithEphemeralPostgreSQL_And_Undefined_PostgreSQL_Image_In_ } _, err := DeploymentCreator(workflow) assert.Error(t, err) - assert.Equal(t, "platform persistence configuration is undefined", err.Error()) + assert.Equal(t, "no persistence specification available in the workflow or in the platform", err.Error()) } diff --git a/controllers/profiles/common/persistence/postgresql.go b/controllers/profiles/common/persistence/postgresql.go index 7098b03f2..a15495e7a 100644 --- a/controllers/profiles/common/persistence/postgresql.go +++ b/controllers/profiles/common/persistence/postgresql.go @@ -66,7 +66,7 @@ func (s *syncConfig) SetConfig(config *operatorapi.PlatformPersistenceSpec) { s.config = config } -func (s *syncConfig) GetPostgreSQLConfiguration() *operatorapi.PostgreSQLPlatformSpec { +func (s *syncConfig) GetPostgreSQLConfiguration() *operatorapi.PersistencePostgreSQL { s.m.Lock() defer s.m.Unlock() if s.config != nil && s.config.PostgreSQL != nil { @@ -75,62 +75,7 @@ func (s *syncConfig) GetPostgreSQLConfiguration() *operatorapi.PostgreSQLPlatfor return nil } -func ConfigurePostgreSQLEnvFromPlatformSpec(spec *operatorapi.PostgreSQLPlatformSpec, schema string) []corev1.EnvVar { - - var namespace string - if len(spec.ServiceRef.Namespace) > 0 { - namespace = fmt.Sprintf(".%s", spec.ServiceRef.Namespace) - } - dataSourceURL := fmt.Sprintf("jdbc:postgresql://%s%s:%d/%s?currentSchema=%s", spec.ServiceRef.Name, namespace, spec.ServiceRef.Port, spec.DatabaseName, schema) - - secretRef := corev1.LocalObjectReference{ - Name: spec.SecretRef.Name, - } - quarkusDatasourceUsername := spec.SecretRef.UserKey - quarkusDatasourcePassword := spec.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_DATASOURCE_JDBC_URL", - Value: dataSourceURL, - }, - { - Name: "KOGITO_PERSISTENCE_TYPE", - Value: "jdbc", - }, - { - Name: "KOGITO_PERSISTENCE_PROTO_MARSHALLER", - Value: "false", - }, - { - Name: "KOGITO_PERSISTENCE_QUERY_TIMEOUT_MILLIS", - Value: "10000", - }, - } -} - -func ConfigurePostgreSQLEnv(postgresql *operatorapi.PersistencePostgreSql, databaseSchema, databaseNamespace string) []corev1.EnvVar { +func ConfigurePostgreSQLEnv(postgresql *operatorapi.PersistencePostgreSQL, databaseSchema, databaseNamespace string) []corev1.EnvVar { dataSourcePort := constants.DefaultPostgreSQLPort databaseName := defaultDatabaseName dataSourceURL := postgresql.JdbcUrl diff --git a/controllers/profiles/common/properties/application_test.go b/controllers/profiles/common/properties/application_test.go index ea865b319..a067ec26b 100644 --- a/controllers/profiles/common/properties/application_test.go +++ b/controllers/profiles/common/properties/application_test.go @@ -670,9 +670,9 @@ func setJobServiceJDBC(jdbc string) plfmOptionFn { if p.Spec.Services.JobService.Persistence == nil { p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceOptions{} } - if p.Spec.Services.JobService.Persistence.PostgreSql == nil { - p.Spec.Services.JobService.Persistence.PostgreSql = &operatorapi.PersistencePostgreSql{} + if p.Spec.Services.JobService.Persistence.PostgreSQL == nil { + p.Spec.Services.JobService.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} } - p.Spec.Services.JobService.Persistence.PostgreSql.JdbcUrl = jdbc + p.Spec.Services.JobService.Persistence.PostgreSQL.JdbcUrl = jdbc } } diff --git a/controllers/sonataflowplatform_controller_test.go b/controllers/sonataflowplatform_controller_test.go index 9cdd79ae6..fd2551da4 100644 --- a/controllers/sonataflowplatform_controller_test.go +++ b/controllers/sonataflowplatform_controller_test.go @@ -137,9 +137,9 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NotContains(t, dep.Spec.Template.Spec.Containers[0].Env, env) // Check with persistence set - ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceOptions{PostgreSql: &v1alpha08.PersistencePostgreSql{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "test"}, - ServiceRef: &v1alpha08.PostgreSqlServiceOptions{Name: "test"}, + ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceOptions{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, }} // Ensure correct container overriding anything set in PodSpec ksp.Spec.Services.DataIndex.PodTemplate.Container = v1alpha08.ContainerSpec{TerminationMessagePath: "testing"} @@ -224,8 +224,8 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set url := "jdbc:postgresql://host:1234/database?currentSchema=data-index-service" - ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceOptions{PostgreSql: &v1alpha08.PersistencePostgreSql{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "test"}, + ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceOptions{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: url, }} // Ensure correct container overriding anything set in PodSpec @@ -250,6 +250,9 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.Contains(t, dep.Spec.Template.Spec.Containers[0].Env, env2) }) + var ( + postgreSQLPort int = 5432 + ) t.Run("verify that persistence options are correctly reconciled when defined in the platform", func(t *testing.T) { namespace := t.Name() // Create a SonataFlowPlatform object with metadata and spec. @@ -261,10 +264,9 @@ func TestSonataFlowPlatformController(t *testing.T) { JobService: &v1alpha08.ServiceSpec{}, }, Persistence: &v1alpha08.PlatformPersistenceSpec{ - PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, - ServiceRef: v1alpha08.ServiceReference{Name: "postgresql", Namespace: "default", Port: 5432}, - DatabaseName: "sonataflow", + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, }, }, } @@ -350,26 +352,25 @@ func TestSonataFlowPlatformController(t *testing.T) { Services: v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{ Persistence: &v1alpha08.PersistenceOptions{ - PostgreSql: &v1alpha08.PersistencePostgreSql{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "dataIndex"}, + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "dataIndex"}, JdbcUrl: urlDI, }, }, }, JobService: &v1alpha08.ServiceSpec{ Persistence: &v1alpha08.PersistenceOptions{ - PostgreSql: &v1alpha08.PersistencePostgreSql{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "job"}, + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "job"}, JdbcUrl: urlJS, }, }, }, }, Persistence: &v1alpha08.PlatformPersistenceSpec{ - PostgreSQL: &v1alpha08.PostgreSQLPlatformSpec{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, - ServiceRef: v1alpha08.ServiceReference{Name: "postgresql", Namespace: "default", Port: 5432}, - DatabaseName: "sonataflow", + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, }, }, } @@ -508,9 +509,9 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NotContains(t, dep.Spec.Template.Spec.Containers[0].Env, envDataIndex) // Check with persistence set - ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceOptions{PostgreSql: &v1alpha08.PersistencePostgreSql{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "test"}, - ServiceRef: &v1alpha08.PostgreSqlServiceOptions{Name: "test"}, + ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceOptions{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, }} // Ensure correct container overriding anything set in PodSpec ksp.Spec.Services.JobService.PodTemplate.Container = v1alpha08.ContainerSpec{TerminationMessagePath: "testing"} @@ -593,8 +594,8 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set url := "jdbc:postgresql://host:1234/database?currentSchema=data-index-service" - ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceOptions{PostgreSql: &v1alpha08.PersistencePostgreSql{ - SecretRef: v1alpha08.PostgreSqlSecretOptions{Name: "test"}, + ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceOptions{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: url, }} // Ensure correct container overriding anything set in PodSpec diff --git a/operator.yaml b/operator.yaml index ec1223542..c5d2218b4 100644 --- a/operator.yaml +++ b/operator.yaml @@ -907,14 +907,15 @@ spec: properties: postgresql: description: Connect configured services to a postgresql database. + maxProperties: 2 + minProperties: 2 properties: - databaseName: - default: sonataflow - description: Name of postgresql database to be used. Defaults - to "sonataflow" + jdbcUrl: + description: PostgreSql JDBC URL. Mutually exclusive to serviceRef. + e.g. "jdbc:postgresql://host:port/database?currentSchema=data-index-service" type: string secretRef: - description: SecretRef contains the database user credentials + description: Secret reference to the database user credentials properties: name: description: Name of the postgresql credentials secret. @@ -929,21 +930,27 @@ spec: - name type: object serviceRef: - description: ServiceRef contains the K8s service name and - namespace location of the PostgreSQL service. + 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 contains the name of the kubernetes - service. This field is mandatory. + description: Name of the postgresql k8s service. type: string namespace: - description: Namespace contains the name of the namespace - where the kubernetes service resides. This field is - optional. + description: Namespace of the postgresql k8s service. + Defaults to the SonataFlowPlatform's local namespace. type: string port: - description: Port contains the port number associated - to the kubernetes service. This field is mandatory. + description: Port to use when connecting to the postgresql + k8s service. Defaults to 5432. type: integer required: - name diff --git a/test/e2e/helpers.go b/test/e2e/helpers.go index 88fffc314..345dc2a69 100644 --- a/test/e2e/helpers.go +++ b/test/e2e/helpers.go @@ -162,30 +162,6 @@ func verifyWorkflowIsAddressable(workflowName string, targetNamespace string) bo } } -func verifyDatabaseConnectionsHealthStatusInPod(name string, namespace string) { - // iterate over all containers to find the one that responds to the HTTP health endpoint - Expect(name).NotTo(BeEmpty(), "pod name is empty") - cmd := exec.Command("kubectl", "get", "pod", name, "-n", namespace, "-o", `jsonpath={.spec.containers[*].name}`) - output, err := utils.Run(cmd) - Expect(err).NotTo(HaveOccurred()) - var errs error - for _, cname := range strings.Split(string(output), " ") { - var h *health - h, err = getHealthStatusInContainer(name, cname, namespace) - if err == nil { - for _, c := range h.Checks { - if c.Name == "Database connections health check" { - Expect(c.Status).To(Equal(upStatus)) - return - } - } - errs = fmt.Errorf("no database connection health status found; %w", errs) - } - errs = fmt.Errorf("%v; %w", err, errs) - } - Expect(errs).NotTo(HaveOccurred(), fmt.Sprintf("No container was found that could respond to the health endpoint %v", errs)) -} - const ( minikubePlatform = "minikube" openshiftPlatform = "openshift" diff --git a/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml index 7f211ae80..0204e50f0 100644 --- a/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml +++ b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml @@ -31,7 +31,7 @@ spec: serviceRef: name: postgres port: 5432 - databaseName: sonataflow + databaseName: sonataflow services: dataIndex: enabled: false diff --git a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml index ed8a9481f..a8013e600 100644 --- a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml +++ b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml @@ -34,7 +34,7 @@ spec: serviceRef: name: no-db-exists port: 5432 - databaseName: find-me + databaseName: find-me services: dataIndex: enabled: false diff --git a/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml index 59faa58fd..f4cb459db 100644 --- a/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml @@ -26,7 +26,8 @@ spec: serviceRef: name: postgres port: 5432 - databaseName: sonataflow + databaseName: sonataflow + databaseSchema: myschema build: template: buildArgs: diff --git a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml index a7342ed12..102195889 100644 --- a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml @@ -26,7 +26,8 @@ spec: serviceRef: name: db_not_defined port: 3456 - databaseName: db_name + databaseName: db_name + databaseSchema: myschema build: template: buildArgs: From d235ca1a617b4d069cec5101fecd06f72e2ef1f0 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 2 Feb 2024 14:49:23 +0100 Subject: [PATCH 13/22] refactor platform CR retrieval Signed-off-by: Jordi Gil --- api/v1alpha08/sonataflow_types.go | 2 +- .../sonataflowplatform_services_types.go | 10 +- api/v1alpha08/sonataflowplatform_types.go | 6 +- api/v1alpha08/zz_generated.deepcopy.go | 34 +--- ...taflow-operator.clusterserviceversion.yaml | 2 +- .../sonataflow.org_sonataflowplatforms.yaml | 2 - .../manifests/sonataflow.org_sonataflows.yaml | 1 - .../sonataflow.org_sonataflowplatforms.yaml | 2 - .../crd/bases/sonataflow.org_sonataflows.yaml | 1 - controllers/platform/initialize.go | 8 - controllers/platform/k8s.go | 10 +- .../services/properties_services_test.go | 4 +- .../profiles/common/constants/objects.go | 24 +++ controllers/profiles/common/ensurer.go | 8 +- .../profiles/common/mutate_visitors.go | 8 +- .../profiles/common/object_creators.go | 71 +++------ .../profiles/common/object_creators_test.go | 150 +++++++++++------- .../profiles/common/persistence/postgresql.go | 45 ++---- .../common/properties/application_test.go | 2 +- controllers/profiles/common/reconciler.go | 9 +- controllers/profiles/common/variables/k8s.go | 24 +++ .../profiles/dev/object_creators_dev.go | 18 +-- .../profiles/dev/object_creators_dev_test.go | 2 +- controllers/profiles/dev/profile_dev_test.go | 36 ++--- controllers/profiles/dev/states_dev.go | 22 ++- .../profiles/dev/status_enricher_dev_test.go | 6 +- .../profiles/prod/deployment_handler.go | 9 +- .../profiles/prod/deployment_handler_test.go | 8 +- .../profiles/prod/profile_prod_test.go | 26 +-- controllers/profiles/prod/states_prod.go | 8 +- .../profiles/prod/states_prod_nobuild.go | 6 +- controllers/profiles/profile.go | 4 +- controllers/sonataflow_controller.go | 7 +- .../sonataflowplatform_controller_test.go | 16 +- operator.yaml | 5 +- test/e2e/workflow_test.go | 2 +- .../ephemeral/02-sonataflow_platform.yaml | 4 - .../by_service/02-sonataflow_platform.yaml | 2 +- 38 files changed, 295 insertions(+), 309 deletions(-) create mode 100644 controllers/profiles/common/constants/objects.go create mode 100644 controllers/profiles/common/variables/k8s.go diff --git a/api/v1alpha08/sonataflow_types.go b/api/v1alpha08/sonataflow_types.go index 306ebe0fc..5233da15a 100644 --- a/api/v1alpha08/sonataflow_types.go +++ b/api/v1alpha08/sonataflow_types.go @@ -658,7 +658,7 @@ type SonataFlowSpec struct { //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="podTemplate" PodTemplate PodTemplateSpec `json:"podTemplate,omitempty"` // Persistence defines the database persistence configuration for the workflow - Persistence *PersistenceOptions `json:"persistence,omitempty"` + Persistence *PersistenceSpec `json:"persistence,omitempty"` } // SonataFlowStatus defines the observed state of SonataFlow diff --git a/api/v1alpha08/sonataflowplatform_services_types.go b/api/v1alpha08/sonataflowplatform_services_types.go index 80260f853..753aa4db8 100644 --- a/api/v1alpha08/sonataflowplatform_services_types.go +++ b/api/v1alpha08/sonataflowplatform_services_types.go @@ -32,20 +32,12 @@ type ServiceSpec struct { Enabled *bool `json:"enabled,omitempty"` // Persists service to a datasource of choice. Ephemeral by default. // +optional - Persistence *PersistenceOptions `json:"persistence,omitempty"` + Persistence *PersistenceSpec `json:"persistence,omitempty"` // PodTemplate describes the deployment details of this platform service instance. //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="podTemplate" PodTemplate PodTemplateSpec `json:"podTemplate,omitempty"` } -// PersistenceOptions configure the services to persist to a datasource of choice -// +kubebuilder:validation:MaxProperties=1 -type PersistenceOptions struct { - // Connect configured services to a postgresql database. - // +optional - PostgreSQL *PersistencePostgreSQL `json:"postgresql,omitempty"` -} - // PersistencePostgreSQL configure postgresql connection for service(s). // +kubebuilder:validation:MinProperties=2 // +kubebuilder:validation:MaxProperties=2 diff --git a/api/v1alpha08/sonataflowplatform_types.go b/api/v1alpha08/sonataflowplatform_types.go index 81b6279a1..5ce126ce3 100644 --- a/api/v1alpha08/sonataflowplatform_types.go +++ b/api/v1alpha08/sonataflowplatform_types.go @@ -51,15 +51,15 @@ type SonataFlowPlatformSpec struct { // the configuration is used as the persistence for platform services and sonataflow instances // that don't provide one of their own. // +optional - Persistence *PlatformPersistenceSpec `json:"persistence,omitempty"` + Persistence *PersistenceSpec `json:"persistence,omitempty"` } -// PlatformPersistenceSpec configures the DataBase support for both platform services and workflows. For services, it allows +// PersistenceSpec configures the DataBase support for both platform services and workflows. For services, it allows // configuring a generic database connectivity if the service does not come with its own configured. In case of workflows, // the operator will add the necessary JDBC properties to in the workflow's application.properties so that it can communicate // with the persistence service based on the spec provided here. // +optional -type PlatformPersistenceSpec struct { +type PersistenceSpec struct { // Connect configured services to a postgresql database. // +optional PostgreSQL *PersistencePostgreSQL `json:"postgresql,omitempty"` diff --git a/api/v1alpha08/zz_generated.deepcopy.go b/api/v1alpha08/zz_generated.deepcopy.go index ed0358e73..a0a66447c 100644 --- a/api/v1alpha08/zz_generated.deepcopy.go +++ b/api/v1alpha08/zz_generated.deepcopy.go @@ -319,26 +319,6 @@ func (in *Flow) DeepCopy() *Flow { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PersistenceOptions) DeepCopyInto(out *PersistenceOptions) { - *out = *in - if in.PostgreSQL != nil { - in, out := &in.PostgreSQL, &out.PostgreSQL - *out = new(PersistencePostgreSQL) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistenceOptions. -func (in *PersistenceOptions) DeepCopy() *PersistenceOptions { - if in == nil { - return nil - } - out := new(PersistenceOptions) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PersistencePostgreSQL) DeepCopyInto(out *PersistencePostgreSQL) { *out = *in @@ -361,7 +341,7 @@ func (in *PersistencePostgreSQL) DeepCopy() *PersistencePostgreSQL { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PlatformPersistenceSpec) DeepCopyInto(out *PlatformPersistenceSpec) { +func (in *PersistenceSpec) DeepCopyInto(out *PersistenceSpec) { *out = *in if in.PostgreSQL != nil { in, out := &in.PostgreSQL, &out.PostgreSQL @@ -370,12 +350,12 @@ func (in *PlatformPersistenceSpec) DeepCopyInto(out *PlatformPersistenceSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlatformPersistenceSpec. -func (in *PlatformPersistenceSpec) DeepCopy() *PlatformPersistenceSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistenceSpec. +func (in *PersistenceSpec) DeepCopy() *PersistenceSpec { if in == nil { return nil } - out := new(PlatformPersistenceSpec) + out := new(PersistenceSpec) in.DeepCopyInto(out) return out } @@ -665,7 +645,7 @@ func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { } if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PersistenceOptions) + *out = new(PersistenceSpec) (*in).DeepCopyInto(*out) } in.PodTemplate.DeepCopyInto(&out.PodTemplate) @@ -1054,7 +1034,7 @@ func (in *SonataFlowPlatformSpec) DeepCopyInto(out *SonataFlowPlatformSpec) { } if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PlatformPersistenceSpec) + *out = new(PersistenceSpec) (*in).DeepCopyInto(*out) } } @@ -1105,7 +1085,7 @@ func (in *SonataFlowSpec) DeepCopyInto(out *SonataFlowSpec) { in.PodTemplate.DeepCopyInto(&out.PodTemplate) if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PersistenceOptions) + *out = new(PersistenceSpec) (*in).DeepCopyInto(*out) } } diff --git a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml index 95bceb338..8d9645043 100644 --- a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml +++ b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml @@ -723,7 +723,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: quay.io/kiegroup/kogito-serverless-operator-nightly:latest + image: quay.io/jordigilh/kogito-serverless-operator-nightly:latest livenessProbe: httpGet: path: /healthz diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml index 39b444fcd..be9a64a73 100644 --- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml +++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml @@ -498,7 +498,6 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. - maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql @@ -8370,7 +8369,6 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. - maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql diff --git a/bundle/manifests/sonataflow.org_sonataflows.yaml b/bundle/manifests/sonataflow.org_sonataflows.yaml index 76c704fb3..23bd17a76 100644 --- a/bundle/manifests/sonataflow.org_sonataflows.yaml +++ b/bundle/manifests/sonataflow.org_sonataflows.yaml @@ -2105,7 +2105,6 @@ spec: persistence: description: Persistence defines the database persistence configuration for the workflow - maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql database. diff --git a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml index 5823fdaf2..25e4f505b 100644 --- a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml +++ b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml @@ -499,7 +499,6 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. - maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql @@ -8371,7 +8370,6 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. - maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql diff --git a/config/crd/bases/sonataflow.org_sonataflows.yaml b/config/crd/bases/sonataflow.org_sonataflows.yaml index e5214329e..65dd887ff 100644 --- a/config/crd/bases/sonataflow.org_sonataflows.yaml +++ b/config/crd/bases/sonataflow.org_sonataflows.yaml @@ -2106,7 +2106,6 @@ spec: persistence: description: Persistence defines the database persistence configuration for the workflow - maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql database. diff --git a/controllers/platform/initialize.go b/controllers/platform/initialize.go index 771eac210..492cd0790 100644 --- a/controllers/platform/initialize.go +++ b/controllers/platform/initialize.go @@ -30,7 +30,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/apache/incubator-kie-kogito-serverless-operator/api" - "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/persistence" "github.com/apache/incubator-kie-kogito-serverless-operator/api/metadata" @@ -107,13 +106,6 @@ func (action *initializeAction) Handle(ctx context.Context, platform *operatorap } platform.Status.Version = metadata.SpecVersion - // store workflow persistence configuration - if platform.Spec.Persistence != nil { - persistence.WorkflowConfig.SetConfig(platform.Spec.Persistence) - } else { - persistence.WorkflowConfig.SetConfig(nil) - } - return platform, nil } diff --git a/controllers/platform/k8s.go b/controllers/platform/k8s.go index 5a853f6a7..1ebe707df 100644 --- a/controllers/platform/k8s.go +++ b/controllers/platform/k8s.go @@ -25,8 +25,8 @@ import ( operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08" "github.com/apache/incubator-kie-kogito-serverless-operator/container-builder/client" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/platform/services" - "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants" + "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/variables" "github.com/apache/incubator-kie-kogito-serverless-operator/log" "github.com/apache/incubator-kie-kogito-serverless-operator/utils" kubeutil "github.com/apache/incubator-kie-kogito-serverless-operator/utils/kubernetes" @@ -94,8 +94,8 @@ func createOrUpdateDeployment(ctx context.Context, client client.Client, platfor readyProbe := &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Path: common.QuarkusHealthPathReady, - Port: common.DefaultHTTPWorkflowPortIntStr, + Path: constants.QuarkusHealthPathReady, + Port: variables.DefaultHTTPWorkflowPortIntStr, Scheme: corev1.URISchemeHTTP, }, }, @@ -106,7 +106,7 @@ func createOrUpdateDeployment(ctx context.Context, client client.Client, platfor FailureThreshold: int32(4), } liveProbe := readyProbe.DeepCopy() - liveProbe.ProbeHandler.HTTPGet.Path = common.QuarkusHealthPathLive + liveProbe.ProbeHandler.HTTPGet.Path = constants.QuarkusHealthPathLive imageTag := psh.GetServiceImageName(constants.PersistenceTypeEphemeral) dataDeployContainer := &corev1.Container{ Image: imageTag, @@ -204,7 +204,7 @@ func createOrUpdateService(ctx context.Context, client client.Client, platform * Name: utils.HttpScheme, Protocol: corev1.ProtocolTCP, Port: 80, - TargetPort: common.DefaultHTTPWorkflowPortIntStr, + TargetPort: variables.DefaultHTTPWorkflowPortIntStr, }, }, Selector: selectorLbl, diff --git a/controllers/platform/services/properties_services_test.go b/controllers/platform/services/properties_services_test.go index 08dfe6d00..b0e81821a 100644 --- a/controllers/platform/services/properties_services_test.go +++ b/controllers/platform/services/properties_services_test.go @@ -223,7 +223,7 @@ func setJobServiceJDBC(jdbc string) plfmOptionFn { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } if p.Spec.Services.JobService.Persistence == nil { - p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceOptions{} + p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceSpec{} } if p.Spec.Services.JobService.Persistence.PostgreSQL == nil { p.Spec.Services.JobService.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} @@ -238,7 +238,7 @@ func setDataIndexJDBC(jdbc string) plfmOptionFn { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } if p.Spec.Services.DataIndex.Persistence == nil { - p.Spec.Services.DataIndex.Persistence = &operatorapi.PersistenceOptions{} + p.Spec.Services.DataIndex.Persistence = &operatorapi.PersistenceSpec{} } if p.Spec.Services.DataIndex.Persistence.PostgreSQL == nil { p.Spec.Services.DataIndex.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} diff --git a/controllers/profiles/common/constants/objects.go b/controllers/profiles/common/constants/objects.go new file mode 100644 index 000000000..caddd1a10 --- /dev/null +++ b/controllers/profiles/common/constants/objects.go @@ -0,0 +1,24 @@ +// 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 constants + +const ( + QuarkusHealthPathReady = "/q/health/ready" + QuarkusHealthPathLive = "/q/health/live" + + // Quarkus Health Check Probe configuration. + // See: https://quarkus.io/guides/smallrye-health#running-the-health-check + QuarkusHealthPathStarted = "/q/health/started" +) diff --git a/controllers/profiles/common/ensurer.go b/controllers/profiles/common/ensurer.go index 7d258dbb3..7e2d63ec6 100644 --- a/controllers/profiles/common/ensurer.go +++ b/controllers/profiles/common/ensurer.go @@ -34,7 +34,7 @@ var _ ObjectEnsurer = &defaultObjectEnsurer{} var _ ObjectEnsurer = &noopObjectEnsurer{} type ObjectEnsurer interface { - Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) + Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) } type ObjectEnsurerWithPlatform interface { Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, platform *operatorapi.SonataFlowPlatform, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) @@ -73,10 +73,10 @@ type defaultObjectEnsurer struct { creator ObjectCreator } -func (d *defaultObjectEnsurer) Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) { +func (d *defaultObjectEnsurer) Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) { result := controllerutil.OperationResultNone - object, err := d.creator(workflow) + object, err := d.creator(workflow, plf) if err != nil { return nil, result, err } @@ -132,7 +132,7 @@ func NewNoopObjectEnsurer() ObjectEnsurer { type noopObjectEnsurer struct { } -func (d *noopObjectEnsurer) Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) { +func (d *noopObjectEnsurer) Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) { result := controllerutil.OperationResultNone return nil, result, nil } diff --git a/controllers/profiles/common/mutate_visitors.go b/controllers/profiles/common/mutate_visitors.go index 8ccbcab35..d264cd595 100644 --- a/controllers/profiles/common/mutate_visitors.go +++ b/controllers/profiles/common/mutate_visitors.go @@ -57,13 +57,13 @@ func ImageDeploymentMutateVisitor(workflow *operatorapi.SonataFlow, image string } // DeploymentMutateVisitor guarantees the state of the default Deployment object -func DeploymentMutateVisitor(workflow *operatorapi.SonataFlow) MutateVisitor { +func DeploymentMutateVisitor(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) MutateVisitor { return func(object client.Object) controllerutil.MutateFn { return func() error { if kubeutil.IsObjectNew(object) { return nil } - original, err := DeploymentCreator(workflow) + original, err := DeploymentCreator(workflow, plf) if err != nil { return err } @@ -94,7 +94,7 @@ func ServiceMutateVisitor(workflow *operatorapi.SonataFlow) MutateVisitor { if kubeutil.IsObjectNew(object) { return nil } - original, err := ServiceCreator(workflow) + original, err := ServiceCreator(workflow, nil) if err != nil { return err } @@ -122,7 +122,7 @@ func ManagedPropertiesMutateVisitor(ctx context.Context, catalog discovery.Servi userProperties = "" } // In the future, if this needs change, instead we can receive an AppPropertyHandler in this mutator - props, err := properties.NewAppPropertyHandler(workflow, platform) + props, err := properties.NewAppPropertyHandler(workflow, plf) if err != nil { return err } diff --git a/controllers/profiles/common/object_creators.go b/controllers/profiles/common/object_creators.go index 98f1e9661..0aa72a4e5 100644 --- a/controllers/profiles/common/object_creators.go +++ b/controllers/profiles/common/object_creators.go @@ -20,19 +20,17 @@ package common import ( - "fmt" - "github.com/imdario/mergo" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/client" 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/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/properties" + "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/variables" "github.com/apache/incubator-kie-kogito-serverless-operator/utils" kubeutil "github.com/apache/incubator-kie-kogito-serverless-operator/utils/kubernetes" "github.com/apache/incubator-kie-kogito-serverless-operator/utils/openshift" @@ -41,7 +39,7 @@ import ( // ObjectCreator is the func that creates the initial reference object, if the object doesn't exist in the cluster, this one is created. // Can be used as a reference to keep the object immutable -type ObjectCreator func(workflow *operatorapi.SonataFlow) (client.Object, error) +type ObjectCreator func(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (client.Object, error) // ObjectCreatorWithPlatform is the func equivalent to ObjectCreator to use when the resource being created needs a reference to the // SonataFlowPlatform @@ -50,13 +48,6 @@ type ObjectCreatorWithPlatform func(workflow *operatorapi.SonataFlow, platform * const ( defaultHTTPServicePort = 80 - // Quarkus Health Check Probe configuration. - // See: https://quarkus.io/guides/smallrye-health#running-the-health-check - - quarkusHealthPathStarted = "/q/health/started" - QuarkusHealthPathReady = "/q/health/ready" - QuarkusHealthPathLive = "/q/health/live" - // Default deployment health check configuration // See: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ @@ -67,13 +58,9 @@ const ( defaultSchemaName = "default" ) -var ( - DefaultHTTPWorkflowPortIntStr = intstr.FromInt(constants.DefaultHTTPWorkflowPortInt) -) - // DeploymentCreator is an objectCreator for a base Kubernetes Deployments for profiles that need to deploy the workflow on a vanilla deployment. // It serves as a basis for a basic Quarkus Java application, expected to listen on http 8080. -func DeploymentCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { +func DeploymentCreator(workflow *operatorapi.SonataFlow, platform *operatorapi.SonataFlowPlatform) (client.Object, error) { lbl := workflowproj.GetDefaultLabels(workflow) deployment := &appsv1.Deployment{ @@ -99,8 +86,7 @@ func DeploymentCreator(workflow *operatorapi.SonataFlow) (client.Object, error) if err := mergo.Merge(&deployment.Spec.Template.Spec, workflow.Spec.PodTemplate.PodSpec.ToPodSpec(), mergo.WithOverride); err != nil { return nil, err } - - flowContainer, err := defaultContainer(workflow) + flowContainer, err := defaultContainer(workflow, platform) if err != nil { return nil, err } @@ -117,9 +103,9 @@ func getReplicasOrDefault(workflow *operatorapi.SonataFlow) *int32 { return workflow.Spec.PodTemplate.Replicas } -func defaultContainer(workflow *operatorapi.SonataFlow) (*corev1.Container, error) { +func defaultContainer(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (*corev1.Container, error) { defaultContainerPort := corev1.ContainerPort{ - ContainerPort: DefaultHTTPWorkflowPortIntStr.IntVal, + ContainerPort: variables.DefaultHTTPWorkflowPortIntStr.IntVal, Name: utils.HttpScheme, Protocol: corev1.ProtocolTCP, } @@ -130,8 +116,8 @@ func defaultContainer(workflow *operatorapi.SonataFlow) (*corev1.Container, erro LivenessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Path: QuarkusHealthPathLive, - Port: DefaultHTTPWorkflowPortIntStr, + Path: constants.QuarkusHealthPathLive, + Port: variables.DefaultHTTPWorkflowPortIntStr, }, }, TimeoutSeconds: healthTimeoutSeconds, @@ -139,8 +125,8 @@ func defaultContainer(workflow *operatorapi.SonataFlow) (*corev1.Container, erro ReadinessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Path: QuarkusHealthPathReady, - Port: DefaultHTTPWorkflowPortIntStr, + Path: constants.QuarkusHealthPathReady, + Port: variables.DefaultHTTPWorkflowPortIntStr, }, }, TimeoutSeconds: healthTimeoutSeconds, @@ -148,8 +134,8 @@ func defaultContainer(workflow *operatorapi.SonataFlow) (*corev1.Container, erro StartupProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Path: quarkusHealthPathStarted, - Port: DefaultHTTPWorkflowPortIntStr, + Path: constants.QuarkusHealthPathStarted, + Port: variables.DefaultHTTPWorkflowPortIntStr, }, }, InitialDelaySeconds: healthStartedInitialDelaySeconds, @@ -164,8 +150,12 @@ func defaultContainer(workflow *operatorapi.SonataFlow) (*corev1.Container, erro return nil, err } if workflow.Spec.Persistence != nil { + c := workflow.Spec.Persistence + if c.PostgreSQL == nil { + c = plf.Spec.Persistence + } var err error - defaultFlowContainer, err = ConfigurePersistence(defaultFlowContainer, workflow.Spec.Persistence, workflow.Name, workflow.Namespace) + defaultFlowContainer, err = persistence.ConfigurePersistence(defaultFlowContainer, c, workflow.Name, workflow.Namespace) if err != nil { return nil, err } @@ -175,7 +165,7 @@ func defaultContainer(workflow *operatorapi.SonataFlow) (*corev1.Container, erro portIdx := -1 for i := range defaultFlowContainer.Ports { if defaultFlowContainer.Ports[i].Name == utils.HttpScheme || - defaultFlowContainer.Ports[i].ContainerPort == DefaultHTTPWorkflowPortIntStr.IntVal { + defaultFlowContainer.Ports[i].ContainerPort == variables.DefaultHTTPWorkflowPortIntStr.IntVal { portIdx = i break } @@ -191,7 +181,7 @@ func defaultContainer(workflow *operatorapi.SonataFlow) (*corev1.Container, erro // ServiceCreator is an objectCreator for a basic Service aiming a vanilla Kubernetes Deployment. // It maps the default HTTP port (80) to the target Java application webserver on port 8080. -func ServiceCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { +func ServiceCreator(workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (client.Object, error) { lbl := workflowproj.GetDefaultLabels(workflow) service := &corev1.Service{ @@ -205,7 +195,7 @@ func ServiceCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { Ports: []corev1.ServicePort{{ Protocol: corev1.ProtocolTCP, Port: defaultHTTPServicePort, - TargetPort: DefaultHTTPWorkflowPortIntStr, + TargetPort: variables.DefaultHTTPWorkflowPortIntStr, }}, }, } @@ -216,7 +206,7 @@ func ServiceCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { // OpenShiftRouteCreator is an ObjectCreator for a basic Route for a workflow running on OpenShift. // It enables the exposition of the service using an OpenShift Route. // See: https://github.com/openshift/api/blob/d170fcdc0fa638b664e4f35f2daf753cb4afe36b/route/v1/route.crd.yaml -func OpenShiftRouteCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { +func OpenShiftRouteCreator(workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (client.Object, error) { route, err := openshift.RouteForWorkflow(workflow) return route, err } @@ -234,22 +224,3 @@ func ManagedPropsConfigMapCreator(workflow *operatorapi.SonataFlow, platform *op } return workflowproj.CreateNewManagedPropsConfigMap(workflow, props), nil } - -func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistenceOptions, defaultSchema, namespace string) (*corev1.Container, error) { - if config == nil { - return serviceContainer, nil - } - c := serviceContainer.DeepCopy() - - var p *operatorapi.PersistencePostgreSQL - if config.PostgreSQL == nil && persistence.WorkflowConfig.GetPostgreSQLConfiguration() == nil { - return nil, fmt.Errorf("no persistence specification available in the workflow or in the platform") - } - if config.PostgreSQL != nil { - p = config.PostgreSQL - } else if persistence.WorkflowConfig.GetPostgreSQLConfiguration() != nil { - p = persistence.WorkflowConfig.GetPostgreSQLConfiguration() - } - c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p, defaultSchema, namespace)...) - return c, nil -} diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index 1ae39eaec..e1e1c9782 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -28,7 +28,6 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/persistence" "github.com/apache/incubator-kie-kogito-serverless-operator/utils" kubeutil "github.com/apache/incubator-kie-kogito-serverless-operator/utils/kubernetes" @@ -129,7 +128,7 @@ func TestMergePodSpec(t *testing.T) { }, } - object, err := DeploymentCreator(workflow) + object, err := DeploymentCreator(workflow, nil) assert.NoError(t, err) deployment := object.(*appsv1.Deployment) @@ -164,7 +163,7 @@ func TestMergePodSpec_OverrideContainers(t *testing.T) { }, } - object, err := DeploymentCreator(workflow) + object, err := DeploymentCreator(workflow, nil) assert.NoError(t, err) deployment := object.(*appsv1.Deployment) @@ -214,7 +213,7 @@ func TestMergePodSpec_WithPostgreSQL_and_JDBC_URL_field(t *testing.T) { }, }, }, - Persistence: &v1alpha08.PersistenceOptions{ + Persistence: &v1alpha08.PersistenceSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: "jdbc:postgresql://host:port/database?currentSchema=workflow", @@ -222,7 +221,7 @@ func TestMergePodSpec_WithPostgreSQL_and_JDBC_URL_field(t *testing.T) { }, } - object, err := DeploymentCreator(workflow) + object, err := DeploymentCreator(workflow, nil) assert.NoError(t, err) deployment := object.(*appsv1.Deployment) @@ -304,7 +303,7 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_In_Workflow_CR(t *testin }, }, }, - Persistence: &v1alpha08.PersistenceOptions{ + Persistence: &v1alpha08.PersistenceSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ @@ -317,7 +316,7 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_In_Workflow_CR(t *testin }, } - object, err := DeploymentCreator(workflow) + object, err := DeploymentCreator(workflow, nil) assert.NoError(t, err) deployment := object.(*appsv1.Deployment) @@ -369,27 +368,36 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_In_Workflow_CR(t *testin } func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesting_It(t *testing.T) { - persistence.WorkflowConfig.SetConfig( - &v1alpha08.PlatformPersistenceSpec{ - PostgreSQL: &v1alpha08.PersistencePostgreSQL{ - SecretRef: v1alpha08.PostgreSQLSecretOptions{ - Name: "foo_secret", - UserKey: "username", - PasswordKey: "password", - }, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ - Name: "service_name", - Namespace: "service_namespace", - Port: &postgreSQLPort, - DatabaseName: "foo", - DatabaseSchema: "bar", + p := &v1alpha08.SonataFlowPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: v1alpha08.SonataFlowPlatformSpec{ + Persistence: &v1alpha08.PersistenceSpec{ + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{ + Name: "foo_secret", + UserKey: "username", + PasswordKey: "password", + }, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ + Name: "service_name", + Namespace: "service_namespace", + Port: &postgreSQLPort, + DatabaseName: "foo", + DatabaseSchema: "bar", + }, }, - }}) + }, + }, + } + workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ - Persistence: &v1alpha08.PersistenceOptions{}, + Persistence: &v1alpha08.PersistenceSpec{}, } - object, err := DeploymentCreator(workflow) + object, err := DeploymentCreator(workflow, p) assert.NoError(t, err) deployment := object.(*appsv1.Deployment) @@ -441,22 +449,30 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti } func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *testing.T) { - persistence.WorkflowConfig.SetConfig( - &v1alpha08.PlatformPersistenceSpec{ - PostgreSQL: &v1alpha08.PersistencePostgreSQL{ - SecretRef: v1alpha08.PostgreSQLSecretOptions{ - Name: "foo_secret", - UserKey: "username", - PasswordKey: "password", - }, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ - Name: "service_name", - Namespace: "service_namespace", - Port: &postgreSQLPort, - DatabaseName: "foo", - DatabaseSchema: "bar", + p := &v1alpha08.SonataFlowPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: v1alpha08.SonataFlowPlatformSpec{ + Persistence: &v1alpha08.PersistenceSpec{ + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{ + Name: "foo_secret", + UserKey: "username", + PasswordKey: "password", + }, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ + Name: "service_name", + Namespace: "service_namespace", + Port: &postgreSQLPort, + DatabaseName: "foo", + DatabaseSchema: "bar", + }, }, - }}) + }, + }, + } workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ PodTemplate: v1alpha08.PodTemplateSpec{ @@ -476,7 +492,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t }, }, }, - Persistence: &v1alpha08.PersistenceOptions{ + Persistence: &v1alpha08.PersistenceSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ @@ -488,7 +504,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t }, }, } - object, err := DeploymentCreator(workflow) + object, err := DeploymentCreator(workflow, p) assert.NoError(t, err) deployment := object.(*appsv1.Deployment) @@ -540,26 +556,34 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t } func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Requesting_it(t *testing.T) { - persistence.WorkflowConfig.SetConfig( - &v1alpha08.PlatformPersistenceSpec{ - PostgreSQL: &v1alpha08.PersistencePostgreSQL{ - SecretRef: v1alpha08.PostgreSQLSecretOptions{ - Name: "foo_secret", - UserKey: "username", - PasswordKey: "password", - }, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ - Name: "service_name", - Namespace: "service_namespace", - Port: &postgreSQLPort, - DatabaseName: "foo", + p := &v1alpha08.SonataFlowPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: v1alpha08.SonataFlowPlatformSpec{ + Persistence: &v1alpha08.PersistenceSpec{ + PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{ + Name: "foo_secret", + UserKey: "username", + PasswordKey: "password", + }, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ + Name: "service_name", + Namespace: "service_namespace", + Port: &postgreSQLPort, + DatabaseName: "foo", + }, }, - }}) + }, + }, + } workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ Persistence: nil, } - object, err := DeploymentCreator(workflow) + object, err := DeploymentCreator(workflow, p) assert.NoError(t, err) deployment := object.(*appsv1.Deployment) @@ -571,12 +595,18 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Req } func TestMergePodSpec_WithEphemeralPostgreSQL_And_Undefined_PostgreSQL_Image_In_Platform_Spec(t *testing.T) { - persistence.WorkflowConfig.SetConfig(nil) + p := &v1alpha08.SonataFlowPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: v1alpha08.SonataFlowPlatformSpec{}, + } workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ - Persistence: &v1alpha08.PersistenceOptions{}, + Persistence: &v1alpha08.PersistenceSpec{}, } - _, err := DeploymentCreator(workflow) + _, err := DeploymentCreator(workflow, p) assert.Error(t, err) - assert.Equal(t, "no persistence specification available in the workflow or in the platform", err.Error()) + assert.Equal(t, "no persistence specification found", err.Error()) } diff --git a/controllers/profiles/common/persistence/postgresql.go b/controllers/profiles/common/persistence/postgresql.go index a15495e7a..f0f77a2ca 100644 --- a/controllers/profiles/common/persistence/postgresql.go +++ b/controllers/profiles/common/persistence/postgresql.go @@ -16,7 +16,6 @@ package persistence import ( "fmt" - "sync" corev1 "k8s.io/api/core/v1" @@ -43,38 +42,6 @@ const ( defaultPostgresSQLPassword = "sonataflow" ) -var ( - WorkflowConfig *syncConfig -) - -func init() { - WorkflowConfig = newSyncConfig() -} - -type syncConfig struct { - m sync.Mutex - config *operatorapi.PlatformPersistenceSpec -} - -func newSyncConfig() *syncConfig { - return &syncConfig{m: sync.Mutex{}} -} - -func (s *syncConfig) SetConfig(config *operatorapi.PlatformPersistenceSpec) { - s.m.Lock() - defer s.m.Unlock() - s.config = config -} - -func (s *syncConfig) GetPostgreSQLConfiguration() *operatorapi.PersistencePostgreSQL { - s.m.Lock() - defer s.m.Unlock() - if s.config != nil && s.config.PostgreSQL != nil { - return s.config.PostgreSQL - } - return nil -} - func ConfigurePostgreSQLEnv(postgresql *operatorapi.PersistencePostgreSQL, databaseSchema, databaseNamespace string) []corev1.EnvVar { dataSourcePort := constants.DefaultPostgreSQLPort databaseName := defaultDatabaseName @@ -146,3 +113,15 @@ func ConfigurePostgreSQLEnv(postgresql *operatorapi.PersistencePostgreSQL, datab }, } } + +func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistenceSpec, defaultSchema, namespace string) (*corev1.Container, error) { + if config == nil { + return nil, fmt.Errorf("no persistence specification found") + } + if config.PostgreSQL == nil { + return nil, fmt.Errorf("no postgreSQL configuration found") + } + c := serviceContainer.DeepCopy() + c.Env = append(c.Env, ConfigurePostgreSQLEnv(config.PostgreSQL, defaultSchema, namespace)...) + return c, nil +} diff --git a/controllers/profiles/common/properties/application_test.go b/controllers/profiles/common/properties/application_test.go index a067ec26b..7ad62c3e2 100644 --- a/controllers/profiles/common/properties/application_test.go +++ b/controllers/profiles/common/properties/application_test.go @@ -668,7 +668,7 @@ func setJobServiceJDBC(jdbc string) plfmOptionFn { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } if p.Spec.Services.JobService.Persistence == nil { - p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceOptions{} + p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceSpec{} } if p.Spec.Services.JobService.Persistence.PostgreSQL == nil { p.Spec.Services.JobService.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} diff --git a/controllers/profiles/common/reconciler.go b/controllers/profiles/common/reconciler.go index 64ecd07ac..7deb259ec 100644 --- a/controllers/profiles/common/reconciler.go +++ b/controllers/profiles/common/reconciler.go @@ -24,6 +24,7 @@ import ( "fmt" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/discovery" + "k8s.io/client-go/tools/record" "k8s.io/klog/v2" @@ -69,9 +70,9 @@ func NewReconciler(support *StateSupport, stateMachine *ReconciliationStateMachi } // Reconcile does the actual reconciliation algorithm based on a set of ReconciliationState -func (b *Reconciler) Reconcile(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, error) { +func (b *Reconciler) Reconcile(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, error) { workflow.Status.Manager().InitializeConditions() - result, objects, err := b.reconciliationStateMachine.do(ctx, workflow) + result, objects, err := b.reconciliationStateMachine.do(ctx, workflow, plf) if err != nil { return result, err } @@ -96,11 +97,11 @@ type ReconciliationStateMachine struct { states []profiles.ReconciliationState } -func (r *ReconciliationStateMachine) do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { +func (r *ReconciliationStateMachine) do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { for _, h := range r.states { if h.CanReconcile(workflow) { klog.V(log.I).InfoS("Found a condition to reconcile.", "Conditions", workflow.Status.Conditions) - result, objs, err := h.Do(ctx, workflow) + result, objs, err := h.Do(ctx, workflow, plf) if err != nil { return result, objs, err } diff --git a/controllers/profiles/common/variables/k8s.go b/controllers/profiles/common/variables/k8s.go new file mode 100644 index 000000000..e15a27986 --- /dev/null +++ b/controllers/profiles/common/variables/k8s.go @@ -0,0 +1,24 @@ +// 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 variables + +import ( + "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants" + "k8s.io/apimachinery/pkg/util/intstr" +) + +var ( + DefaultHTTPWorkflowPortIntStr = intstr.FromInt(constants.DefaultHTTPWorkflowPortInt) +) diff --git a/controllers/profiles/dev/object_creators_dev.go b/controllers/profiles/dev/object_creators_dev.go index c4cf92549..0db4b96d6 100644 --- a/controllers/profiles/dev/object_creators_dev.go +++ b/controllers/profiles/dev/object_creators_dev.go @@ -44,8 +44,8 @@ const ( // aiming a vanilla Kubernetes Deployment. // It maps the default HTTP port (80) to the target Java application webserver on port 8080. // It configures the Service as a NodePort type service, in this way it will be easier for a developer access the service -func serviceCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { - object, _ := common.ServiceCreator(workflow) +func serviceCreator(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (client.Object, error) { + object, _ := common.ServiceCreator(workflow, plf) service := object.(*corev1.Service) // Let's double-check that the workflow is using the Dev Profile we would like to expose it via NodePort if profiles.IsDevProfile(workflow) { @@ -54,8 +54,8 @@ func serviceCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { return service, nil } -func deploymentCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { - obj, err := common.DeploymentCreator(workflow) +func deploymentCreator(workflow *operatorapi.SonataFlow, platform *operatorapi.SonataFlowPlatform) (client.Object, error) { + obj, err := common.DeploymentCreator(workflow, platform) if err != nil { return nil, err } @@ -74,7 +74,7 @@ func deploymentCreator(workflow *operatorapi.SonataFlow) (client.Object, error) } // workflowDefConfigMapCreator creates a new ConfigMap that holds the definition of a workflow specification. -func workflowDefConfigMapCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { +func workflowDefConfigMapCreator(workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (client.Object, error) { configMap, err := workflowdef.CreateNewConfigMap(workflow) if err != nil { return nil, err @@ -84,13 +84,13 @@ func workflowDefConfigMapCreator(workflow *operatorapi.SonataFlow) (client.Objec } // deploymentMutateVisitor guarantees the state of the default Deployment object -func deploymentMutateVisitor(workflow *operatorapi.SonataFlow) common.MutateVisitor { +func deploymentMutateVisitor(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) common.MutateVisitor { return func(object client.Object) controllerutil.MutateFn { return func() error { if kubeutil.IsObjectNew(object) { return nil } - original, err := deploymentCreator(workflow) + original, err := deploymentCreator(workflow, plf) if err != nil { return err } @@ -100,13 +100,13 @@ func deploymentMutateVisitor(workflow *operatorapi.SonataFlow) common.MutateVisi } } -func ensureWorkflowDefConfigMapMutator(workflow *operatorapi.SonataFlow) common.MutateVisitor { +func ensureWorkflowDefConfigMapMutator(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) common.MutateVisitor { return func(object client.Object) controllerutil.MutateFn { return func() error { if kubeutil.IsObjectNew(object) { return nil } - original, err := workflowDefConfigMapCreator(workflow) + original, err := workflowDefConfigMapCreator(workflow, plf) if err != nil { return err } diff --git a/controllers/profiles/dev/object_creators_dev_test.go b/controllers/profiles/dev/object_creators_dev_test.go index 8209940a2..0a35499c3 100644 --- a/controllers/profiles/dev/object_creators_dev_test.go +++ b/controllers/profiles/dev/object_creators_dev_test.go @@ -31,7 +31,7 @@ import ( func Test_ensureWorkflowDevServiceIsExposed(t *testing.T) { workflow := test.GetBaseSonataFlowWithDevProfile(t.Name()) //On Kubernetes we want the service exposed in Dev with NodePort - service, _ := serviceCreator(workflow) + service, _ := serviceCreator(workflow, nil) service.SetUID("1") service.SetResourceVersion("1") diff --git a/controllers/profiles/dev/profile_dev_test.go b/controllers/profiles/dev/profile_dev_test.go index 1c3478ea0..d7fd5478f 100644 --- a/controllers/profiles/dev/profile_dev_test.go +++ b/controllers/profiles/dev/profile_dev_test.go @@ -60,7 +60,7 @@ func Test_OverrideStartupProbe(t *testing.T) { devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow) + result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -71,7 +71,7 @@ func Test_OverrideStartupProbe(t *testing.T) { deployment.Spec.Template.Spec.Containers[0].StartupProbe.FailureThreshold = newThreshold assert.NoError(t, client.Update(context.TODO(), deployment)) // reconcile and fetch from the cluster - result, err = devReconciler.Reconcile(context.TODO(), workflow) + result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) deployment = test.MustGetDeployment(t, client, workflow) @@ -88,14 +88,14 @@ func Test_recoverFromFailureNoDeployment(t *testing.T) { reconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) // we are in failed state and have no objects - result, err := reconciler.Reconcile(context.TODO(), workflow) + result, err := reconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) // the recover state tried to clear the conditions of our workflow, so we can try reconciling it again workflow = test.MustGetWorkflow(t, client, workflowID) assert.True(t, workflow.Status.GetTopLevelCondition().IsUnknown()) - result, err = reconciler.Reconcile(context.TODO(), workflow) + result, err = reconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -109,7 +109,7 @@ func Test_recoverFromFailureNoDeployment(t *testing.T) { assert.NoError(t, err) // the fake client won't update the deployment status condition since we don't have a deployment controller // our state will think that we don't have a deployment available yet, so it will try to reset the pods - result, err = reconciler.Reconcile(context.TODO(), workflow) + result, err = reconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -128,7 +128,7 @@ func Test_newDevProfile(t *testing.T) { devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow) + result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -169,7 +169,7 @@ func Test_newDevProfile(t *testing.T) { assert.NoError(t, err) // reconcile again - result, err = devReconciler.Reconcile(context.TODO(), workflow) + result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -196,7 +196,7 @@ func Test_newDevProfile(t *testing.T) { workflow.Status.Manager().MarkTrue(api.RunningConditionType) err = client.Status().Update(context.TODO(), workflow) assert.NoError(t, err) - result, err = devReconciler.Reconcile(context.TODO(), workflow) + result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -209,7 +209,7 @@ func Test_devProfileImageDefaultsNoPlatform(t *testing.T) { client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow).WithStatusSubresource(workflow).Build() devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow) + result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -226,7 +226,7 @@ func Test_devProfileWithImageSnapshotOverrideWithPlatform(t *testing.T) { client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, platform).WithStatusSubresource(workflow, platform).Build() devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow) + result, err := devReconciler.Reconcile(context.TODO(), workflow, platform) assert.NoError(t, err) assert.NotNil(t, result) @@ -243,7 +243,7 @@ func Test_devProfileWithWPlatformWithoutDevBaseImageAndWithBaseImage(t *testing. client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, platform).WithStatusSubresource(workflow, platform).Build() devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow) + result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -260,7 +260,7 @@ func Test_devProfileWithPlatformWithoutDevBaseImageAndWithoutBaseImage(t *testin client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, platform).WithStatusSubresource(workflow, platform).Build() devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow) + result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -305,7 +305,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { errCreate := client.Create(context.Background(), cmUser) assert.Nil(t, errCreate) - result, err := devReconciler.Reconcile(context.TODO(), workflow) + result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -330,7 +330,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { workflow.Status.Manager().MarkTrue(api.RunningConditionType) err = client.Update(context.TODO(), workflow) assert.NoError(t, err) - result, err = devReconciler.Reconcile(context.TODO(), workflow) + result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -346,7 +346,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { workflow.Status.Manager().MarkTrue(api.RunningConditionType) err = client.Update(context.TODO(), workflow) assert.NoError(t, err) - result, err = devReconciler.Reconcile(context.TODO(), workflow) + result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -362,7 +362,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { workflow.Status.Manager().MarkTrue(api.RunningConditionType) err = client.Status().Update(context.TODO(), workflow) assert.NoError(t, err) - result, err = devReconciler.Reconcile(context.TODO(), workflow) + result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, api.ExternalResourcesNotFoundReason, workflow.Status.GetTopLevelCondition().Reason) @@ -370,7 +370,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { // delete the link workflow.Spec.Resources.ConfigMaps = nil assert.NoError(t, client.Update(context.TODO(), workflow)) - result, err = devReconciler.Reconcile(context.TODO(), workflow) + result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) @@ -393,7 +393,7 @@ func Test_VolumeWithCapitalizedPaths(t *testing.T) { devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow) + result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) assert.NoError(t, err) assert.NotNil(t, result) diff --git a/controllers/profiles/dev/states_dev.go b/controllers/profiles/dev/states_dev.go index f98e03eb2..55a4eb5a7 100644 --- a/controllers/profiles/dev/states_dev.go +++ b/controllers/profiles/dev/states_dev.go @@ -34,7 +34,6 @@ import ( "github.com/apache/incubator-kie-kogito-serverless-operator/api" operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08" - "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/platform" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/workflowdef" @@ -59,10 +58,10 @@ func (e *ensureRunningWorkflowState) CanReconcile(workflow *operatorapi.SonataFl return workflow.Status.IsReady() || workflow.Status.GetTopLevelCondition().IsUnknown() || workflow.Status.IsChildObjectsProblem() } -func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { +func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { var objs []client.Object - flowDefCM, _, err := e.ensurers.definitionConfigMap.Ensure(ctx, workflow, ensureWorkflowDefConfigMapMutator(workflow)) + flowDefCM, _, err := e.ensurers.definitionConfigMap.Ensure(ctx, workflow, plf, ensureWorkflowDefConfigMapMutator(workflow, plf)) if err != nil { return ctrl.Result{Requeue: false}, objs, err } @@ -70,9 +69,8 @@ func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatora devBaseContainerImage := workflowdef.GetDefaultWorkflowDevModeImageTag() // check if the Platform available - pl, err := platform.GetActivePlatform(ctx, e.C, workflow.Namespace) - if err == nil && len(pl.Spec.DevMode.BaseImage) > 0 { - devBaseContainerImage = pl.Spec.DevMode.BaseImage + if plf != nil && len(plf.Spec.DevMode.BaseImage) > 0 { + devBaseContainerImage = plf.Spec.DevMode.BaseImage } userPropsCM, _, err := e.ensurers.userPropsConfigMap.Ensure(ctx, workflow) if err != nil { @@ -93,8 +91,8 @@ func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatora return ctrl.Result{RequeueAfter: constants.RequeueAfterFailure}, objs, nil } - deployment, _, err := e.ensurers.deployment.Ensure(ctx, workflow, - deploymentMutateVisitor(workflow), + deployment, _, err := e.ensurers.deployment.Ensure(ctx, workflow, plf, + deploymentMutateVisitor(workflow, plf), common.ImageDeploymentMutateVisitor(workflow, devBaseContainerImage), mountDevConfigMapsMutateVisitor(workflow, flowDefCM.(*corev1.ConfigMap), userPropsCM.(*corev1.ConfigMap), managedPropsCM.(*corev1.ConfigMap), externalCM)) if err != nil { @@ -102,13 +100,13 @@ func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatora } objs = append(objs, deployment) - service, _, err := e.ensurers.service.Ensure(ctx, workflow, common.ServiceMutateVisitor(workflow)) + service, _, err := e.ensurers.service.Ensure(ctx, workflow, plf, common.ServiceMutateVisitor(workflow)) if err != nil { return ctrl.Result{RequeueAfter: constants.RequeueAfterFailure}, objs, err } objs = append(objs, service) - route, _, err := e.ensurers.network.Ensure(ctx, workflow) + route, _, err := e.ensurers.network.Ensure(ctx, workflow, plf) if err != nil { return ctrl.Result{RequeueAfter: constants.RequeueAfterFailure}, objs, err } @@ -153,7 +151,7 @@ func (f *followWorkflowDeploymentState) CanReconcile(workflow *operatorapi.Sonat return workflow.Status.IsWaitingForDeployment() } -func (f *followWorkflowDeploymentState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { +func (f *followWorkflowDeploymentState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { result, err := common.DeploymentManager(f.C).SyncDeploymentStatus(ctx, workflow) if err != nil { return ctrl.Result{RequeueAfter: constants.RequeueAfterFailure}, nil, err @@ -191,7 +189,7 @@ func (r *recoverFromFailureState) CanReconcile(workflow *operatorapi.SonataFlow) return workflow.Status.GetCondition(api.RunningConditionType).IsFalse() } -func (r *recoverFromFailureState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { +func (r *recoverFromFailureState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { // for now, a very basic attempt to recover by rolling out the deployment deployment := &appsv1.Deployment{} if err := r.C.Get(ctx, client.ObjectKeyFromObject(workflow), deployment); err != nil { diff --git a/controllers/profiles/dev/status_enricher_dev_test.go b/controllers/profiles/dev/status_enricher_dev_test.go index 39e4570df..5da675d47 100644 --- a/controllers/profiles/dev/status_enricher_dev_test.go +++ b/controllers/profiles/dev/status_enricher_dev_test.go @@ -39,7 +39,7 @@ func Test_enrichmentStatusOnK8s(t *testing.T) { workflow := test.GetBaseSonataFlowWithDevProfile(t.Name()) workflow.Namespace = toK8SNamespace(t.Name()) - service, _ := common.ServiceCreator(workflow) + service, _ := common.ServiceCreator(workflow, nil) client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, service).Build() obj, err := statusEnricher(context.TODO(), client, workflow) @@ -55,7 +55,7 @@ func Test_enrichmentStatusOnK8s(t *testing.T) { workflow := test.GetBaseSonataFlowWithDevProfile(t.Name()) workflow.Namespace = t.Name() - service, _ := serviceCreator(workflow) + service, _ := serviceCreator(workflow, nil) client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, service).Build() _, err := statusEnricher(context.TODO(), client, workflow) assert.Error(t, err) @@ -67,7 +67,7 @@ func Test_enrichmentStatusOnOCP(t *testing.T) { t.Run("verify that the service URL is returned with the default cluster name on default namespace", func(t *testing.T) { workflow := test.GetBaseSonataFlowWithDevProfile(t.Name()) workflow.Namespace = toK8SNamespace(t.Name()) - service, _ := serviceCreator(workflow) + service, _ := serviceCreator(workflow, nil) route := &openshiftv1.Route{} route.Name = workflow.Name route.Namespace = workflow.Namespace diff --git a/controllers/profiles/prod/deployment_handler.go b/controllers/profiles/prod/deployment_handler.go index 3b693429d..3f885e976 100644 --- a/controllers/profiles/prod/deployment_handler.go +++ b/controllers/profiles/prod/deployment_handler.go @@ -43,8 +43,8 @@ func newDeploymentReconciler(stateSupport *common.StateSupport, ensurer *objectE } } -func (d *deploymentReconciler) reconcile(ctx context.Context, workflow *operatorapi.SonataFlow) (reconcile.Result, []client.Object, error) { - return d.reconcileWithBuiltImage(ctx, workflow, "") +func (d *deploymentReconciler) reconcile(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (reconcile.Result, []client.Object, error) { + return d.reconcileWithBuiltImage(ctx, workflow, plf, "") } func (d *deploymentReconciler) reconcileWithBuiltImage(ctx context.Context, workflow *operatorapi.SonataFlow, image string) (reconcile.Result, []client.Object, error) { @@ -75,7 +75,7 @@ func (d *deploymentReconciler) reconcileWithBuiltImage(ctx context.Context, work return reconcile.Result{}, nil, err } - service, _, err := d.ensurers.service.Ensure(ctx, workflow, common.ServiceMutateVisitor(workflow)) + service, _, err := d.ensurers.service.Ensure(ctx, workflow, plf, common.ServiceMutateVisitor(workflow)) if err != nil { workflow.Status.Manager().MarkFalse(api.RunningConditionType, api.DeploymentUnavailableReason, "Unable to make the service available due to ", err) _, err = d.PerformStatusUpdate(ctx, workflow) @@ -106,6 +106,7 @@ func (d *deploymentReconciler) reconcileWithBuiltImage(ctx context.Context, work func (d *deploymentReconciler) getDeploymentMutateVisitors( workflow *operatorapi.SonataFlow, + plf *operatorapi.SonataFlowPlatform, image string, userPropsCM *v1.ConfigMap, managedPropsCM *v1.ConfigMap) []common.MutateVisitor { @@ -117,7 +118,7 @@ func (d *deploymentReconciler) getDeploymentMutateVisitors( common.RolloutDeploymentIfCMChangedMutateVisitor(workflow, userPropsCM, managedPropsCM), } } - return []common.MutateVisitor{common.DeploymentMutateVisitor(workflow), + return []common.MutateVisitor{common.DeploymentMutateVisitor(workflow, plf), common.ImageDeploymentMutateVisitor(workflow, image), mountProdConfigMapsMutateVisitor(workflow, userPropsCM, managedPropsCM), common.RolloutDeploymentIfCMChangedMutateVisitor(workflow, userPropsCM, managedPropsCM)} diff --git a/controllers/profiles/prod/deployment_handler_test.go b/controllers/profiles/prod/deployment_handler_test.go index a133b9635..d11b7bcc1 100644 --- a/controllers/profiles/prod/deployment_handler_test.go +++ b/controllers/profiles/prod/deployment_handler_test.go @@ -40,7 +40,7 @@ func Test_CheckPodTemplateChangesReflectDeployment(t *testing.T) { stateSupport := fakeReconcilerSupport(client) handler := newDeploymentReconciler(stateSupport, newObjectEnsurers(stateSupport)) - result, objects, err := handler.reconcile(context.TODO(), workflow) + result, objects, err := handler.reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.NotEmpty(t, objects) assert.True(t, result.Requeue) @@ -49,7 +49,7 @@ func Test_CheckPodTemplateChangesReflectDeployment(t *testing.T) { expectedImg := "quay.io/apache/my-new-workflow:1.0.0" workflow.Spec.PodTemplate.Container.Image = expectedImg utilruntime.Must(client.Update(context.TODO(), workflow)) - result, objects, err = handler.reconcile(context.TODO(), workflow) + result, objects, err = handler.reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.NotEmpty(t, objects) assert.True(t, result.Requeue) @@ -73,7 +73,7 @@ func Test_CheckDeploymentRolloutAfterCMChange(t *testing.T) { stateSupport := fakeReconcilerSupport(client) handler := newDeploymentReconciler(stateSupport, newObjectEnsurers(stateSupport)) - result, objects, err := handler.reconcile(context.TODO(), workflow) + result, objects, err := handler.reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.NotEmpty(t, objects) assert.True(t, result.Requeue) @@ -135,7 +135,7 @@ func Test_CheckDeploymentUnchangedAfterCMChangeOtherKeys(t *testing.T) { stateSupport := fakeReconcilerSupport(client) handler := newDeploymentReconciler(stateSupport, newObjectEnsurers(stateSupport)) - result, objects, err := handler.reconcile(context.TODO(), workflow) + result, objects, err := handler.reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.NotEmpty(t, objects) assert.True(t, result.Requeue) diff --git a/controllers/profiles/prod/profile_prod_test.go b/controllers/profiles/prod/profile_prod_test.go index 21c7ad03a..181461c09 100644 --- a/controllers/profiles/prod/profile_prod_test.go +++ b/controllers/profiles/prod/profile_prod_test.go @@ -35,6 +35,10 @@ import ( clientruntime "sigs.k8s.io/controller-runtime/pkg/client" ) +var ( + emptyPlatform = &operatorapi.SonataFlowPlatform{} +) + func Test_Reconciler_ProdOps(t *testing.T) { workflow := test.GetBaseSonataFlowWithProdOpsProfile(t.Name()) workflow.Spec.PodTemplate.PodSpec.InitContainers = append(workflow.Spec.PodTemplate.PodSpec.InitContainers, corev1.Container{ @@ -45,7 +49,7 @@ func Test_Reconciler_ProdOps(t *testing.T) { client := test.NewSonataFlowClientBuilder(). WithRuntimeObjects(workflow). WithStatusSubresource(workflow, &operatorapi.SonataFlowBuild{}).Build() - result, err := NewProfileForOpsReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) + result, err := NewProfileForOpsReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.NotNil(t, result.RequeueAfter) @@ -56,7 +60,7 @@ func Test_Reconciler_ProdOps(t *testing.T) { assert.False(t, workflow.Status.IsReady()) // Reconcile again to run the deployment handler - result, err = NewProfileForOpsReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) + result, err = NewProfileForOpsReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) // Let's check for the right creation of the workflow (one CM volume, one container with a custom image) @@ -84,7 +88,7 @@ func Test_Reconciler_ProdCustomPod(t *testing.T) { client := test.NewSonataFlowClientBuilder(). WithRuntimeObjects(workflow, build, platform). WithStatusSubresource(workflow, build, platform).Build() - _, err := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) + _, err := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) // Let's check for the right creation of the workflow (one CM volume, one container with a custom image) @@ -105,7 +109,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { WithRuntimeObjects(workflow, platform). WithStatusSubresource(workflow, platform, &operatorapi.SonataFlowBuild{}).Build() - result, err := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) + result, err := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.NotNil(t, result.RequeueAfter) @@ -113,7 +117,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { assert.False(t, workflow.Status.IsReady()) // still building - result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) + result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.Equal(t, requeueWhileWaitForBuild, result.RequeueAfter) assert.True(t, workflow.Status.IsBuildRunningOrUnknown()) @@ -126,7 +130,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { assert.NoError(t, client.Status().Update(context.TODO(), build)) // last reconciliation cycle waiting for build - result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) + result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.Equal(t, requeueWhileWaitForBuild, result.RequeueAfter) assert.False(t, workflow.Status.IsBuildRunningOrUnknown()) @@ -134,7 +138,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { assert.Equal(t, api.WaitingForDeploymentReason, workflow.Status.GetTopLevelCondition().Reason) // now we create the objects - result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) + result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.False(t, workflow.Status.IsBuildRunningOrUnknown()) assert.False(t, workflow.Status.IsReady()) @@ -152,7 +156,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { err = client.Status().Update(context.TODO(), deployment) assert.NoError(t, err) - result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) + result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) assert.NoError(t, err) assert.False(t, workflow.Status.IsBuildRunningOrUnknown()) assert.True(t, workflow.Status.IsReady()) @@ -170,7 +174,7 @@ func Test_deployWorkflowReconciliationHandler_handleObjects(t *testing.T) { StateSupport: fakeReconcilerSupport(client), ensurers: newObjectEnsurers(&common.StateSupport{C: client}), } - result, objects, err := handler.Do(context.TODO(), workflow) + result, objects, err := handler.Do(context.TODO(), workflow, emptyPlatform) assert.Greater(t, result.RequeueAfter, int64(0)) assert.NoError(t, err) assert.NotNil(t, result) @@ -199,7 +203,7 @@ func Test_GenerationAnnotationCheck(t *testing.T) { StateSupport: fakeReconcilerSupport(client), ensurers: newObjectEnsurers(&common.StateSupport{C: client}), } - result, objects, err := handler.Do(context.TODO(), workflow) + result, objects, err := handler.Do(context.TODO(), workflow, emptyPlatform) assert.Greater(t, result.RequeueAfter, int64(time.Second)) assert.NoError(t, err) assert.NotNil(t, result) @@ -218,7 +222,7 @@ func Test_GenerationAnnotationCheck(t *testing.T) { StateSupport: fakeReconcilerSupport(client), ensurers: newObjectEnsurers(&common.StateSupport{C: client}), } - result, objects, err = handler.Do(context.TODO(), workflowChanged) + result, objects, err = handler.Do(context.TODO(), workflowChanged, emptyPlatform) assert.NoError(t, err) // no requeue, no objects since the workflow has changed assert.Equal(t, time.Duration(0), result.RequeueAfter) diff --git a/controllers/profiles/prod/states_prod.go b/controllers/profiles/prod/states_prod.go index feb30dca6..199cbd423 100644 --- a/controllers/profiles/prod/states_prod.go +++ b/controllers/profiles/prod/states_prod.go @@ -48,7 +48,7 @@ func (h *newBuilderState) CanReconcile(workflow *operatorapi.SonataFlow) bool { workflow.Status.IsBuildFailed() } -func (h *newBuilderState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { +func (h *newBuilderState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { _, err := platform.GetActivePlatform(ctx, h.C, workflow.Namespace) if err != nil { if errors.IsNotFound(err) { @@ -100,7 +100,7 @@ func (h *followBuildStatusState) CanReconcile(workflow *operatorapi.SonataFlow) return workflow.Status.IsBuildRunningOrUnknown() || workflow.Status.IsWaitingForBuild() } -func (h *followBuildStatusState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { +func (h *followBuildStatusState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { // Let's retrieve the build to check the status build, err := builder.NewSonataFlowBuildManager(ctx, h.C).GetOrCreateBuild(workflow) if err != nil { @@ -160,7 +160,7 @@ func (h *deployWithBuildWorkflowState) CanReconcile(workflow *operatorapi.Sonata return workflow.Status.GetCondition(api.BuiltConditionType).IsTrue() } -func (h *deployWithBuildWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { +func (h *deployWithBuildWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { // Guard to avoid errors while getting a new builder manager. // Maybe we can do typed errors in the buildManager and // have something like sonataerr.IsPlatformNotFound(err) instead. @@ -189,7 +189,7 @@ func (h *deployWithBuildWorkflowState) Do(ctx context.Context, workflow *operato } // didn't change, business as usual - return newDeploymentReconciler(h.StateSupport, h.ensurers).reconcileWithBuiltImage(ctx, workflow, build.Status.ImageTag) + return newDeploymentReconciler(h.StateSupport, h.ensurers).reconcileWithBuiltImage(ctx, workflow, plf, build.Status.ImageTag) } func (h *deployWithBuildWorkflowState) PostReconcile(ctx context.Context, workflow *operatorapi.SonataFlow) error { diff --git a/controllers/profiles/prod/states_prod_nobuild.go b/controllers/profiles/prod/states_prod_nobuild.go index 46449ce1f..bc4d1b787 100644 --- a/controllers/profiles/prod/states_prod_nobuild.go +++ b/controllers/profiles/prod/states_prod_nobuild.go @@ -36,7 +36,7 @@ func (f *ensureBuildSkipped) CanReconcile(workflow *operatorapi.SonataFlow) bool workflow.Status.GetCondition(api.BuiltConditionType).Reason != api.BuildSkippedReason } -func (f *ensureBuildSkipped) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { +func (f *ensureBuildSkipped) Do(ctx context.Context, workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { // We skip the build, so let's ensure the status reflect that workflow.Status.Manager().MarkFalse(api.BuiltConditionType, api.BuildSkippedReason, "") if _, err := f.PerformStatusUpdate(ctx, workflow); err != nil { @@ -61,8 +61,8 @@ func (f *followDeployWorkflowState) CanReconcile(workflow *operatorapi.SonataFlo return workflow.Status.GetCondition(api.BuiltConditionType).Reason == api.BuildSkippedReason } -func (f *followDeployWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { - return newDeploymentReconciler(f.StateSupport, f.ensurers).reconcile(ctx, workflow) +func (f *followDeployWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { + return newDeploymentReconciler(f.StateSupport, f.ensurers).reconcile(ctx, workflow, plf) } func (f *followDeployWorkflowState) PostReconcile(ctx context.Context, workflow *operatorapi.SonataFlow) error { diff --git a/controllers/profiles/profile.go b/controllers/profiles/profile.go index 794ca7a70..f8c93063d 100644 --- a/controllers/profiles/profile.go +++ b/controllers/profiles/profile.go @@ -59,7 +59,7 @@ import ( // // While debugging, focus on the ReconciliationState(s), not in the profile implementation since the base algorithm is the same for every profile. type ProfileReconciler interface { - Reconcile(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, error) + Reconcile(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, error) GetProfile() metadata.ProfileType } @@ -69,7 +69,7 @@ type ReconciliationState interface { CanReconcile(workflow *operatorapi.SonataFlow) bool // Do perform the reconciliation task. It returns the controller result, the objects updated, and an error if any. // Objects can be nil if the reconciliation state doesn't perform any updates in any Kubernetes object. - Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) + Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) // PostReconcile performs the actions to perform after the reconciliation that are not mandatory PostReconcile(ctx context.Context, workflow *operatorapi.SonataFlow) error } diff --git a/controllers/sonataflow_controller.go b/controllers/sonataflow_controller.go index e635f3b3d..f946eab41 100644 --- a/controllers/sonataflow_controller.go +++ b/controllers/sonataflow_controller.go @@ -94,8 +94,11 @@ func (r *SonataFlowReconciler) Reconcile(ctx context.Context, req ctrl.Request) klog.V(log.I).InfoS("Ignoring request because resource is not assigned to current operator") return reconcile.Result{}, nil } - - return profiles.NewReconciler(r.Client, r.Config, r.Recorder, workflow).Reconcile(ctx, workflow) + plf, err := platform.GetActivePlatform(context.TODO(), r.Client, workflow.Namespace) + if err != nil { + return reconcile.Result{}, err + } + return profiles.NewReconciler(r.Client, r.Config, r.Recorder, workflow).Reconcile(ctx, workflow, plf) } func platformEnqueueRequestsFromMapFunc(c client.Client, p *operatorapi.SonataFlowPlatform) []reconcile.Request { diff --git a/controllers/sonataflowplatform_controller_test.go b/controllers/sonataflowplatform_controller_test.go index fd2551da4..c37957ca0 100644 --- a/controllers/sonataflowplatform_controller_test.go +++ b/controllers/sonataflowplatform_controller_test.go @@ -137,7 +137,7 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NotContains(t, dep.Spec.Template.Spec.Containers[0].Env, env) // Check with persistence set - ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceOptions{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, }} @@ -224,7 +224,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set url := "jdbc:postgresql://host:1234/database?currentSchema=data-index-service" - ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceOptions{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: url, }} @@ -263,7 +263,7 @@ func TestSonataFlowPlatformController(t *testing.T) { DataIndex: &v1alpha08.ServiceSpec{}, JobService: &v1alpha08.ServiceSpec{}, }, - Persistence: &v1alpha08.PlatformPersistenceSpec{ + Persistence: &v1alpha08.PersistenceSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, @@ -351,7 +351,7 @@ func TestSonataFlowPlatformController(t *testing.T) { ksp.Spec = v1alpha08.SonataFlowPlatformSpec{ Services: v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{ - Persistence: &v1alpha08.PersistenceOptions{ + Persistence: &v1alpha08.PersistenceSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "dataIndex"}, JdbcUrl: urlDI, @@ -359,7 +359,7 @@ func TestSonataFlowPlatformController(t *testing.T) { }, }, JobService: &v1alpha08.ServiceSpec{ - Persistence: &v1alpha08.PersistenceOptions{ + Persistence: &v1alpha08.PersistenceSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "job"}, JdbcUrl: urlJS, @@ -367,7 +367,7 @@ func TestSonataFlowPlatformController(t *testing.T) { }, }, }, - Persistence: &v1alpha08.PlatformPersistenceSpec{ + Persistence: &v1alpha08.PersistenceSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, @@ -509,7 +509,7 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NotContains(t, dep.Spec.Template.Spec.Containers[0].Env, envDataIndex) // Check with persistence set - ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceOptions{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, }} @@ -594,7 +594,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set url := "jdbc:postgresql://host:1234/database?currentSchema=data-index-service" - ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceOptions{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: url, }} diff --git a/operator.yaml b/operator.yaml index c5d2218b4..f6e912a6b 100644 --- a/operator.yaml +++ b/operator.yaml @@ -977,7 +977,6 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. - maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql @@ -8849,7 +8848,6 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. - maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql @@ -18922,7 +18920,6 @@ spec: persistence: description: Persistence defines the database persistence configuration for the workflow - maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql database. @@ -26864,7 +26861,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: quay.io/kiegroup/kogito-serverless-operator-nightly:latest + image: quay.io/jordigilh/kogito-serverless-operator-nightly:latest livenessProbe: httpGet: path: /healthz diff --git a/test/e2e/workflow_test.go b/test/e2e/workflow_test.go index 6cbeee7eb..2bca93a5f 100644 --- a/test/e2e/workflow_test.go +++ b/test/e2e/workflow_test.go @@ -208,7 +208,7 @@ var _ = Describe("Validate the persistence ", Ordered, func() { }, 12*time.Minute).Should(BeTrue()) }, Entry("defined in the workflow from an existing kubernetes service as a reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("by_service")), - Entry("defined from the sonataflow platform as reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform")), + FEntry("defined from the sonataflow platform as reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform")), Entry("defined in the workflow and from the sonataflow platform", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_overwritten_by_service")), ) diff --git a/test/testdata/platform/services/prod/ephemeral/02-sonataflow_platform.yaml b/test/testdata/platform/services/prod/ephemeral/02-sonataflow_platform.yaml index 8f77082bc..573cbf720 100644 --- a/test/testdata/platform/services/prod/ephemeral/02-sonataflow_platform.yaml +++ b/test/testdata/platform/services/prod/ephemeral/02-sonataflow_platform.yaml @@ -18,10 +18,6 @@ metadata: name: sonataflow-platform spec: build: - template: - buildArgs: - - name: QUARKUS_EXTENSION - value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:999-SNAPSHOT config: strategyOptions: KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/by_service/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/by_service/02-sonataflow_platform.yaml index 5867f2d60..2de0372b9 100644 --- a/test/testdata/workflow/persistence/by_service/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/by_service/02-sonataflow_platform.yaml @@ -21,7 +21,7 @@ spec: template: buildArgs: - name: QUARKUS_EXTENSIONS - value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:999-SNAPSHOT,org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final + value: org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final config: strategyOptions: KanikoBuildCacheEnabled: "true" From 3a4a5cc80ba3fbf43133a8ca4beeea2a5e9ef04d Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Sun, 4 Feb 2024 11:11:05 +0100 Subject: [PATCH 14/22] Remove focus test function Signed-off-by: Jordi Gil --- config/manager/kustomization.yaml | 2 +- test/e2e/workflow_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index bc6245f05..81b250efb 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -16,7 +16,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: quay.io/kiegroup/kogito-serverless-operator-nightly + newName: quay.io/jordigilh/kogito-serverless-operator-nightly newTag: latest # Patching the manager deployment file to add an env var with the operator namespace in patchesJson6902: diff --git a/test/e2e/workflow_test.go b/test/e2e/workflow_test.go index 2bca93a5f..6cbeee7eb 100644 --- a/test/e2e/workflow_test.go +++ b/test/e2e/workflow_test.go @@ -208,7 +208,7 @@ var _ = Describe("Validate the persistence ", Ordered, func() { }, 12*time.Minute).Should(BeTrue()) }, Entry("defined in the workflow from an existing kubernetes service as a reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("by_service")), - FEntry("defined from the sonataflow platform as reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform")), + Entry("defined from the sonataflow platform as reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform")), Entry("defined in the workflow and from the sonataflow platform", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_overwritten_by_service")), ) From 785ad19cf23d70b2804272e24a4b5c1335e87f59 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Mon, 5 Feb 2024 09:42:08 +0100 Subject: [PATCH 15/22] Refactor logic to retrieve platform CR when deploying workflow to inject persistence configuration when platform provides one Signed-off-by: Jordi Gil --- api/v1alpha08/sonataflow_persistence_types.go | 60 +++++++++++++++++++ api/v1alpha08/sonataflow_types.go | 2 +- .../sonataflowplatform_services_types.go | 47 +-------------- api/v1alpha08/sonataflowplatform_types.go | 6 +- api/v1alpha08/zz_generated.deepcopy.go | 38 ++++++------ api/zz_generated.deepcopy.go | 2 - ...taflow-operator.clusterserviceversion.yaml | 2 +- config/manager/kustomization.yaml | 2 +- controllers/platform/k8s.go | 20 +++---- controllers/platform/platformutils.go | 16 +++-- .../services/properties_services_test.go | 4 +- controllers/platform/services/services.go | 19 +++--- controllers/profiles/common/ensurer.go | 10 ++-- .../profiles/common/mutate_visitors.go | 7 +-- .../profiles/common/object_creators.go | 10 ++-- .../profiles/common/object_creators_test.go | 17 +++--- .../profiles/common/persistence/postgresql.go | 2 +- .../common/properties/application_test.go | 13 +--- controllers/profiles/common/reconciler.go | 8 +-- .../profiles/dev/object_creators_dev.go | 15 ++--- .../profiles/dev/object_creators_dev_test.go | 2 +- controllers/profiles/dev/profile_dev.go | 6 +- controllers/profiles/dev/profile_dev_test.go | 36 +++++------ controllers/profiles/dev/states_dev.go | 20 ++++--- .../profiles/dev/status_enricher_dev_test.go | 6 +- .../profiles/prod/deployment_handler.go | 11 ++-- .../profiles/prod/deployment_handler_test.go | 8 +-- controllers/profiles/prod/profile_prod.go | 4 +- .../profiles/prod/profile_prod_test.go | 22 +++---- controllers/profiles/prod/states_prod.go | 8 +-- .../profiles/prod/states_prod_nobuild.go | 6 +- controllers/profiles/profile.go | 4 +- controllers/sonataflow_controller.go | 6 +- .../sonataflowplatform_controller_test.go | 32 +++++----- operator.yaml | 2 +- .../postgreSQL/02-sonataflow_platform.yaml | 7 +-- 36 files changed, 237 insertions(+), 243 deletions(-) create mode 100644 api/v1alpha08/sonataflow_persistence_types.go diff --git a/api/v1alpha08/sonataflow_persistence_types.go b/api/v1alpha08/sonataflow_persistence_types.go new file mode 100644 index 000000000..f93b7a4ca --- /dev/null +++ b/api/v1alpha08/sonataflow_persistence_types.go @@ -0,0 +1,60 @@ +// 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 v1alpha08 + +// PersistencePostgreSQL configure postgresql connection for service(s). +// +kubebuilder:validation:MinProperties=2 +// +kubebuilder:validation:MaxProperties=2 +type PersistencePostgreSQL struct { + // Secret reference to the database user credentials + SecretRef PostgreSQLSecretOptions `json:"secretRef"` + // Service reference to postgresql datasource. Mutually exclusive to jdbcUrl. + // +optional + ServiceRef *PostgreSQLServiceOptions `json:"serviceRef,omitempty"` + // PostgreSql JDBC URL. Mutually exclusive to serviceRef. + // e.g. "jdbc:postgresql://host:port/database?currentSchema=data-index-service" + // +optional + JdbcUrl string `json:"jdbcUrl,omitempty"` +} + +// PostgreSQLSecretOptions use credential secret for postgresql connection. +type PostgreSQLSecretOptions struct { + // Name of the postgresql credentials secret. + Name string `json:"name"` + // Defaults to POSTGRESQL_USER + // +optional + UserKey string `json:"userKey,omitempty"` + // Defaults to POSTGRESQL_PASSWORD + // +optional + PasswordKey string `json:"passwordKey,omitempty"` +} + +// PostgreSQLServiceOptions use k8s service to configure postgresql jdbc url. +type PostgreSQLServiceOptions struct { + // Name of the postgresql k8s service. + Name string `json:"name"` + // Namespace of the postgresql k8s service. Defaults to the SonataFlowPlatform's local namespace. + // +optional + Namespace string `json:"namespace,omitempty"` + // Port to use when connecting to the postgresql k8s service. Defaults to 5432. + // +optional + Port *int `json:"port,omitempty"` + // Name of postgresql database to be used. Defaults to "sonataflow" + // +optional + DatabaseName string `json:"databaseName,omitempty"` + // Schema of postgresql database to be used. Defaults to "data-index-service" + // +optional + DatabaseSchema string `json:"databaseSchema,omitempty"` +} diff --git a/api/v1alpha08/sonataflow_types.go b/api/v1alpha08/sonataflow_types.go index 5233da15a..644fb0a3a 100644 --- a/api/v1alpha08/sonataflow_types.go +++ b/api/v1alpha08/sonataflow_types.go @@ -658,7 +658,7 @@ type SonataFlowSpec struct { //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="podTemplate" PodTemplate PodTemplateSpec `json:"podTemplate,omitempty"` // Persistence defines the database persistence configuration for the workflow - Persistence *PersistenceSpec `json:"persistence,omitempty"` + Persistence *PersistencePlatformSpec `json:"persistence,omitempty"` } // SonataFlowStatus defines the observed state of SonataFlow diff --git a/api/v1alpha08/sonataflowplatform_services_types.go b/api/v1alpha08/sonataflowplatform_services_types.go index 753aa4db8..ae5e4a99c 100644 --- a/api/v1alpha08/sonataflowplatform_services_types.go +++ b/api/v1alpha08/sonataflowplatform_services_types.go @@ -32,53 +32,8 @@ type ServiceSpec struct { Enabled *bool `json:"enabled,omitempty"` // Persists service to a datasource of choice. Ephemeral by default. // +optional - Persistence *PersistenceSpec `json:"persistence,omitempty"` + Persistence *PersistencePlatformSpec `json:"persistence,omitempty"` // PodTemplate describes the deployment details of this platform service instance. //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="podTemplate" PodTemplate PodTemplateSpec `json:"podTemplate,omitempty"` } - -// PersistencePostgreSQL configure postgresql connection for service(s). -// +kubebuilder:validation:MinProperties=2 -// +kubebuilder:validation:MaxProperties=2 -type PersistencePostgreSQL struct { - // Secret reference to the database user credentials - SecretRef PostgreSQLSecretOptions `json:"secretRef"` - // Service reference to postgresql datasource. Mutually exclusive to jdbcUrl. - // +optional - ServiceRef *PostgreSQLServiceOptions `json:"serviceRef,omitempty"` - // PostgreSql JDBC URL. Mutually exclusive to serviceRef. - // e.g. "jdbc:postgresql://host:port/database?currentSchema=data-index-service" - // +optional - JdbcUrl string `json:"jdbcUrl,omitempty"` -} - -// PostgreSQLSecretOptions use credential secret for postgresql connection. -type PostgreSQLSecretOptions struct { - // Name of the postgresql credentials secret. - Name string `json:"name"` - // Defaults to POSTGRESQL_USER - // +optional - UserKey string `json:"userKey,omitempty"` - // Defaults to POSTGRESQL_PASSWORD - // +optional - PasswordKey string `json:"passwordKey,omitempty"` -} - -// PostgreSQLServiceOptions use k8s service to configure postgresql jdbc url. -type PostgreSQLServiceOptions struct { - // Name of the postgresql k8s service. - Name string `json:"name"` - // Namespace of the postgresql k8s service. Defaults to the SonataFlowPlatform's local namespace. - // +optional - Namespace string `json:"namespace,omitempty"` - // Port to use when connecting to the postgresql k8s service. Defaults to 5432. - // +optional - Port *int `json:"port,omitempty"` - // Name of postgresql database to be used. Defaults to "sonataflow" - // +optional - DatabaseName string `json:"databaseName,omitempty"` - // Schema of postgresql database to be used. Defaults to "data-index-service" - // +optional - DatabaseSchema string `json:"databaseSchema,omitempty"` -} diff --git a/api/v1alpha08/sonataflowplatform_types.go b/api/v1alpha08/sonataflowplatform_types.go index 5ce126ce3..84e701376 100644 --- a/api/v1alpha08/sonataflowplatform_types.go +++ b/api/v1alpha08/sonataflowplatform_types.go @@ -51,15 +51,15 @@ type SonataFlowPlatformSpec struct { // the configuration is used as the persistence for platform services and sonataflow instances // that don't provide one of their own. // +optional - Persistence *PersistenceSpec `json:"persistence,omitempty"` + Persistence *PersistencePlatformSpec `json:"persistence,omitempty"` } -// PersistenceSpec configures the DataBase support for both platform services and workflows. For services, it allows +// PersistencePlatformSpec configures the DataBase support for both platform services and workflows. For services, it allows // configuring a generic database connectivity if the service does not come with its own configured. In case of workflows, // the operator will add the necessary JDBC properties to in the workflow's application.properties so that it can communicate // with the persistence service based on the spec provided here. // +optional -type PersistenceSpec struct { +type PersistencePlatformSpec struct { // Connect configured services to a postgresql database. // +optional PostgreSQL *PersistencePostgreSQL `json:"postgresql,omitempty"` diff --git a/api/v1alpha08/zz_generated.deepcopy.go b/api/v1alpha08/zz_generated.deepcopy.go index a0a66447c..0819ad1f8 100644 --- a/api/v1alpha08/zz_generated.deepcopy.go +++ b/api/v1alpha08/zz_generated.deepcopy.go @@ -22,7 +22,7 @@ package v1alpha08 import ( "github.com/serverlessworkflow/sdk-go/v2/model" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "knative.dev/pkg/apis" @@ -320,42 +320,42 @@ func (in *Flow) DeepCopy() *Flow { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PersistencePostgreSQL) DeepCopyInto(out *PersistencePostgreSQL) { +func (in *PersistencePlatformSpec) DeepCopyInto(out *PersistencePlatformSpec) { *out = *in - out.SecretRef = in.SecretRef - if in.ServiceRef != nil { - in, out := &in.ServiceRef, &out.ServiceRef - *out = new(PostgreSQLServiceOptions) + if in.PostgreSQL != nil { + in, out := &in.PostgreSQL, &out.PostgreSQL + *out = new(PersistencePostgreSQL) (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistencePostgreSQL. -func (in *PersistencePostgreSQL) DeepCopy() *PersistencePostgreSQL { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistencePlatformSpec. +func (in *PersistencePlatformSpec) DeepCopy() *PersistencePlatformSpec { if in == nil { return nil } - out := new(PersistencePostgreSQL) + out := new(PersistencePlatformSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PersistenceSpec) DeepCopyInto(out *PersistenceSpec) { +func (in *PersistencePostgreSQL) DeepCopyInto(out *PersistencePostgreSQL) { *out = *in - if in.PostgreSQL != nil { - in, out := &in.PostgreSQL, &out.PostgreSQL - *out = new(PersistencePostgreSQL) + out.SecretRef = in.SecretRef + if in.ServiceRef != nil { + in, out := &in.ServiceRef, &out.ServiceRef + *out = new(PostgreSQLServiceOptions) (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistenceSpec. -func (in *PersistenceSpec) DeepCopy() *PersistenceSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistencePostgreSQL. +func (in *PersistencePostgreSQL) DeepCopy() *PersistencePostgreSQL { if in == nil { return nil } - out := new(PersistenceSpec) + out := new(PersistencePostgreSQL) in.DeepCopyInto(out) return out } @@ -645,7 +645,7 @@ func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { } if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PersistenceSpec) + *out = new(PersistencePlatformSpec) (*in).DeepCopyInto(*out) } in.PodTemplate.DeepCopyInto(&out.PodTemplate) @@ -1034,7 +1034,7 @@ func (in *SonataFlowPlatformSpec) DeepCopyInto(out *SonataFlowPlatformSpec) { } if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PersistenceSpec) + *out = new(PersistencePlatformSpec) (*in).DeepCopyInto(*out) } } @@ -1085,7 +1085,7 @@ func (in *SonataFlowSpec) DeepCopyInto(out *SonataFlowSpec) { in.PodTemplate.DeepCopyInto(&out.PodTemplate) if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PersistenceSpec) + *out = new(PersistencePlatformSpec) (*in).DeepCopyInto(*out) } } diff --git a/api/zz_generated.deepcopy.go b/api/zz_generated.deepcopy.go index b68499c7b..4fd61d6d9 100644 --- a/api/zz_generated.deepcopy.go +++ b/api/zz_generated.deepcopy.go @@ -20,8 +20,6 @@ package api -import () - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Condition) DeepCopyInto(out *Condition) { *out = *in diff --git a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml index 8d9645043..95bceb338 100644 --- a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml +++ b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml @@ -723,7 +723,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: quay.io/jordigilh/kogito-serverless-operator-nightly:latest + image: quay.io/kiegroup/kogito-serverless-operator-nightly:latest livenessProbe: httpGet: path: /healthz diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 81b250efb..bc6245f05 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -16,7 +16,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: quay.io/jordigilh/kogito-serverless-operator-nightly + newName: quay.io/kiegroup/kogito-serverless-operator-nightly newTag: latest # Patching the manager deployment file to add an env var with the operator namespace in patchesJson6902: diff --git a/controllers/platform/k8s.go b/controllers/platform/k8s.go index 1ebe707df..f2e5313e7 100644 --- a/controllers/platform/k8s.go +++ b/controllers/platform/k8s.go @@ -61,19 +61,17 @@ func (action *serviceAction) Handle(ctx context.Context, platform *operatorapi.S return nil, err } - if platform.Spec.Services != nil { - psDI := services.NewDataIndexHandler(platform) - if psDI.IsServiceSetInSpec() { - if err := createOrUpdateServiceComponents(ctx, action.client, platform, psDI); err != nil { - return nil, err - } + psDI := services.NewDataIndexHandler(platform) + if psDI.IsServiceSetInSpec() { + if err := createOrUpdateServiceComponents(ctx, action.client, platform, psDI); err != nil { + return nil, err } + } - psJS := services.NewJobServiceHandler(platform) - if psJS.IsServiceSetInSpec() { - if err := createOrUpdateServiceComponents(ctx, action.client, platform, psJS); err != nil { - return nil, err - } + psJS := services.NewJobServiceHandler(platform) + if psJS.IsServiceSetInSpec() { + if err := createOrUpdateServiceComponents(ctx, action.client, platform, psJS); err != nil { + return nil, err } } diff --git a/controllers/platform/platformutils.go b/controllers/platform/platformutils.go index ae14c40e9..9249bed01 100644 --- a/controllers/platform/platformutils.go +++ b/controllers/platform/platformutils.go @@ -111,15 +111,13 @@ func setPlatformDefaults(p *operatorapi.SonataFlowPlatform, verbose bool) error } // When dataIndex object set, default to enabled if bool not set - if p.Spec.Services != nil { - var enable = true - if p.Spec.Services.DataIndex != nil && p.Spec.Services.DataIndex.Enabled == nil { - p.Spec.Services.DataIndex.Enabled = &enable - } - // When the JobService field has a value, default to enabled if the `Enabled` field's value is nil - if p.Spec.Services.JobService != nil && p.Spec.Services.JobService.Enabled == nil { - p.Spec.Services.JobService.Enabled = &enable - } + var enable = true + if p.Spec.Services.DataIndex != nil && p.Spec.Services.DataIndex.Enabled == nil { + p.Spec.Services.DataIndex.Enabled = &enable + } + // When the JobService field has a value, default to enabled if the `Enabled` field's value is nil + if p.Spec.Services.JobService != nil && p.Spec.Services.JobService.Enabled == nil { + p.Spec.Services.JobService.Enabled = &enable } setStatusAdditionalInfo(p) diff --git a/controllers/platform/services/properties_services_test.go b/controllers/platform/services/properties_services_test.go index b0e81821a..c0983b0b7 100644 --- a/controllers/platform/services/properties_services_test.go +++ b/controllers/platform/services/properties_services_test.go @@ -223,7 +223,7 @@ func setJobServiceJDBC(jdbc string) plfmOptionFn { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } if p.Spec.Services.JobService.Persistence == nil { - p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceSpec{} + p.Spec.Services.JobService.Persistence = &operatorapi.PersistencePlatformSpec{} } if p.Spec.Services.JobService.Persistence.PostgreSQL == nil { p.Spec.Services.JobService.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} @@ -238,7 +238,7 @@ func setDataIndexJDBC(jdbc string) plfmOptionFn { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } if p.Spec.Services.DataIndex.Persistence == nil { - p.Spec.Services.DataIndex.Persistence = &operatorapi.PersistenceSpec{} + p.Spec.Services.DataIndex.Persistence = &operatorapi.PersistencePlatformSpec{} } if p.Spec.Services.DataIndex.Persistence.PostgreSQL == nil { p.Spec.Services.DataIndex.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} diff --git a/controllers/platform/services/services.go b/controllers/platform/services/services.go index 4ce41687d..d75a5a6bb 100644 --- a/controllers/platform/services/services.go +++ b/controllers/platform/services/services.go @@ -408,21 +408,16 @@ func (j JobServiceHandler) GenerateServiceProperties() (*properties.Properties, props.Set(constants.JobServiceKafkaSmallRyeHealthProperty, "false") // add data source reactive URL if j.hasPostgreSQLConfigured() { - var dataSourceReactiveURL string - var err error jspec := j.platform.Spec.Services.JobService + var pspec *operatorapi.PersistencePostgreSQL if j.IsServiceSetInSpec() && jspec.Persistence != nil && jspec.Persistence.PostgreSQL != nil { - dataSourceReactiveURL, err = generateReactiveURL(j.platform.Spec.Services.JobService.Persistence.PostgreSQL, j.GetServiceName(), j.platform.Namespace, constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) - if err != nil { - return nil, err - } + pspec = j.platform.Spec.Services.JobService.Persistence.PostgreSQL } else { - p := j.platform.Spec.Persistence.PostgreSQL - var namespace string - if len(p.ServiceRef.Namespace) > 0 { - namespace = fmt.Sprintf(".%s", p.ServiceRef.Namespace) - } - dataSourceReactiveURL = fmt.Sprintf("%s://%s%s:%d/%s?search_path=%s", constants.PersistenceTypePostgreSQL, p.ServiceRef.Name, namespace, *p.ServiceRef.Port, p.ServiceRef.DatabaseName, j.GetServiceName()) + pspec = j.platform.Spec.Persistence.PostgreSQL + } + dataSourceReactiveURL, err := generateReactiveURL(pspec, j.GetServiceName(), j.platform.Namespace, constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) + if err != nil { + return nil, err } props.Set(constants.JobServiceDataSourceReactiveURL, dataSourceReactiveURL) } diff --git a/controllers/profiles/common/ensurer.go b/controllers/profiles/common/ensurer.go index 7e2d63ec6..980d5cbf5 100644 --- a/controllers/profiles/common/ensurer.go +++ b/controllers/profiles/common/ensurer.go @@ -34,10 +34,10 @@ var _ ObjectEnsurer = &defaultObjectEnsurer{} var _ ObjectEnsurer = &noopObjectEnsurer{} type ObjectEnsurer interface { - Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) + Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) } type ObjectEnsurerWithPlatform interface { - Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, platform *operatorapi.SonataFlowPlatform, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) + Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, pl *operatorapi.SonataFlowPlatform, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) } // MutateVisitor is a visitor function that mutates the given object before performing any updates in the cluster. @@ -73,10 +73,10 @@ type defaultObjectEnsurer struct { creator ObjectCreator } -func (d *defaultObjectEnsurer) Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) { +func (d *defaultObjectEnsurer) Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) { result := controllerutil.OperationResultNone - object, err := d.creator(workflow, plf) + object, err := d.creator(workflow) if err != nil { return nil, result, err } @@ -132,7 +132,7 @@ func NewNoopObjectEnsurer() ObjectEnsurer { type noopObjectEnsurer struct { } -func (d *noopObjectEnsurer) Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) { +func (d *noopObjectEnsurer) Ensure(ctx context.Context, workflow *operatorapi.SonataFlow, visitors ...MutateVisitor) (client.Object, controllerutil.OperationResult, error) { result := controllerutil.OperationResultNone return nil, result, nil } diff --git a/controllers/profiles/common/mutate_visitors.go b/controllers/profiles/common/mutate_visitors.go index d264cd595..6b70bc0f1 100644 --- a/controllers/profiles/common/mutate_visitors.go +++ b/controllers/profiles/common/mutate_visitors.go @@ -27,7 +27,6 @@ import ( "github.com/imdario/mergo" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -94,7 +93,7 @@ func ServiceMutateVisitor(workflow *operatorapi.SonataFlow) MutateVisitor { if kubeutil.IsObjectNew(object) { return nil } - original, err := ServiceCreator(workflow, nil) + original, err := ServiceCreator(workflow) if err != nil { return err } @@ -106,7 +105,7 @@ func ServiceMutateVisitor(workflow *operatorapi.SonataFlow) MutateVisitor { } func ManagedPropertiesMutateVisitor(ctx context.Context, catalog discovery.ServiceCatalog, - workflow *operatorapi.SonataFlow, platform *operatorapi.SonataFlowPlatform, userProps *corev1.ConfigMap) MutateVisitor { + workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform, userProps *corev1.ConfigMap) MutateVisitor { return func(object client.Object) controllerutil.MutateFn { return func() error { managedProps := object.(*corev1.ConfigMap) @@ -138,7 +137,7 @@ func ManagedPropertiesMutateVisitor(ctx context.Context, catalog discovery.Servi // This method can be used as an alternative to the Kubernetes ConfigMap refresher. // // See: https://kubernetes.io/docs/concepts/configuration/configmap/#mounted-configmaps-are-updated-automatically -func RolloutDeploymentIfCMChangedMutateVisitor(workflow *operatorapi.SonataFlow, userPropsCM *v1.ConfigMap, managedPropsCM *v1.ConfigMap) MutateVisitor { +func RolloutDeploymentIfCMChangedMutateVisitor(workflow *operatorapi.SonataFlow, userPropsCM *corev1.ConfigMap, managedPropsCM *corev1.ConfigMap) MutateVisitor { return func(object client.Object) controllerutil.MutateFn { return func() error { deployment := object.(*appsv1.Deployment) diff --git a/controllers/profiles/common/object_creators.go b/controllers/profiles/common/object_creators.go index 0aa72a4e5..4806ec497 100644 --- a/controllers/profiles/common/object_creators.go +++ b/controllers/profiles/common/object_creators.go @@ -39,7 +39,7 @@ import ( // ObjectCreator is the func that creates the initial reference object, if the object doesn't exist in the cluster, this one is created. // Can be used as a reference to keep the object immutable -type ObjectCreator func(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (client.Object, error) +type ObjectCreator func(workflow *operatorapi.SonataFlow) (client.Object, error) // ObjectCreatorWithPlatform is the func equivalent to ObjectCreator to use when the resource being created needs a reference to the // SonataFlowPlatform @@ -60,7 +60,7 @@ const ( // DeploymentCreator is an objectCreator for a base Kubernetes Deployments for profiles that need to deploy the workflow on a vanilla deployment. // It serves as a basis for a basic Quarkus Java application, expected to listen on http 8080. -func DeploymentCreator(workflow *operatorapi.SonataFlow, platform *operatorapi.SonataFlowPlatform) (client.Object, error) { +func DeploymentCreator(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (client.Object, error) { lbl := workflowproj.GetDefaultLabels(workflow) deployment := &appsv1.Deployment{ @@ -86,7 +86,7 @@ func DeploymentCreator(workflow *operatorapi.SonataFlow, platform *operatorapi.S if err := mergo.Merge(&deployment.Spec.Template.Spec, workflow.Spec.PodTemplate.PodSpec.ToPodSpec(), mergo.WithOverride); err != nil { return nil, err } - flowContainer, err := defaultContainer(workflow, platform) + flowContainer, err := defaultContainer(workflow, plf) if err != nil { return nil, err } @@ -181,7 +181,7 @@ func defaultContainer(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataF // ServiceCreator is an objectCreator for a basic Service aiming a vanilla Kubernetes Deployment. // It maps the default HTTP port (80) to the target Java application webserver on port 8080. -func ServiceCreator(workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (client.Object, error) { +func ServiceCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { lbl := workflowproj.GetDefaultLabels(workflow) service := &corev1.Service{ @@ -206,7 +206,7 @@ func ServiceCreator(workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowP // OpenShiftRouteCreator is an ObjectCreator for a basic Route for a workflow running on OpenShift. // It enables the exposition of the service using an OpenShift Route. // See: https://github.com/openshift/api/blob/d170fcdc0fa638b664e4f35f2daf753cb4afe36b/route/v1/route.crd.yaml -func OpenShiftRouteCreator(workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (client.Object, error) { +func OpenShiftRouteCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { route, err := openshift.RouteForWorkflow(workflow) return route, err } diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index e1e1c9782..9e1a29b4d 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -27,6 +27,7 @@ import ( "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" "github.com/apache/incubator-kie-kogito-serverless-operator/utils" kubeutil "github.com/apache/incubator-kie-kogito-serverless-operator/utils/kubernetes" @@ -213,7 +214,7 @@ func TestMergePodSpec_WithPostgreSQL_and_JDBC_URL_field(t *testing.T) { }, }, }, - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: "jdbc:postgresql://host:port/database?currentSchema=workflow", @@ -303,7 +304,7 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_In_Workflow_CR(t *testin }, }, }, - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ @@ -374,7 +375,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti Namespace: "default", }, Spec: v1alpha08.SonataFlowPlatformSpec{ - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", @@ -395,7 +396,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ - Persistence: &v1alpha08.PersistenceSpec{}, + Persistence: &v1alpha08.PersistencePlatformSpec{}, } object, err := DeploymentCreator(workflow, p) assert.NoError(t, err) @@ -455,7 +456,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t Namespace: "default", }, Spec: v1alpha08.SonataFlowPlatformSpec{ - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", @@ -492,7 +493,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t }, }, }, - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ @@ -562,7 +563,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Req Namespace: "default", }, Spec: v1alpha08.SonataFlowPlatformSpec{ - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", @@ -604,7 +605,7 @@ func TestMergePodSpec_WithEphemeralPostgreSQL_And_Undefined_PostgreSQL_Image_In_ } workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ - Persistence: &v1alpha08.PersistenceSpec{}, + Persistence: &v1alpha08.PersistencePlatformSpec{}, } _, err := DeploymentCreator(workflow, p) assert.Error(t, err) diff --git a/controllers/profiles/common/persistence/postgresql.go b/controllers/profiles/common/persistence/postgresql.go index f0f77a2ca..3ab969634 100644 --- a/controllers/profiles/common/persistence/postgresql.go +++ b/controllers/profiles/common/persistence/postgresql.go @@ -114,7 +114,7 @@ func ConfigurePostgreSQLEnv(postgresql *operatorapi.PersistencePostgreSQL, datab } } -func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistenceSpec, defaultSchema, namespace string) (*corev1.Container, error) { +func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistencePlatformSpec, defaultSchema, namespace string) (*corev1.Container, error) { if config == nil { return nil, fmt.Errorf("no persistence specification found") } diff --git a/controllers/profiles/common/properties/application_test.go b/controllers/profiles/common/properties/application_test.go index 7ad62c3e2..e3780930f 100644 --- a/controllers/profiles/common/properties/application_test.go +++ b/controllers/profiles/common/properties/application_test.go @@ -203,7 +203,7 @@ func Test_appPropertyHandler_WithServicesWithUserOverrides(t *testing.T) { platform := test.GetBasePlatform() platform.Namespace = ns platform.Spec = operatorapi.SonataFlowPlatformSpec{ - Services: &operatorapi.ServicesPlatformSpec{ + Services: operatorapi.ServicesPlatformSpec{ DataIndex: &operatorapi.ServiceSpec{ Enabled: &enabled, }, @@ -625,9 +625,6 @@ func generatePlatform(opts ...plfmOptionFn) *operatorapi.SonataFlowPlatform { func setJobServiceEnabledValue(v *bool) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { - if p.Spec.Services == nil { - p.Spec.Services = &operatorapi.ServicesPlatformSpec{} - } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } @@ -637,9 +634,6 @@ func setJobServiceEnabledValue(v *bool) plfmOptionFn { func setDataIndexEnabledValue(v *bool) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { - if p.Spec.Services == nil { - p.Spec.Services = &operatorapi.ServicesPlatformSpec{} - } if p.Spec.Services.DataIndex == nil { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } @@ -661,14 +655,11 @@ func setPlatformName(name string) plfmOptionFn { func setJobServiceJDBC(jdbc string) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { - if p.Spec.Services == nil { - p.Spec.Services = &operatorapi.ServicesPlatformSpec{} - } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } if p.Spec.Services.JobService.Persistence == nil { - p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceSpec{} + p.Spec.Services.JobService.Persistence = &operatorapi.PersistencePlatformSpec{} } if p.Spec.Services.JobService.Persistence.PostgreSQL == nil { p.Spec.Services.JobService.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} diff --git a/controllers/profiles/common/reconciler.go b/controllers/profiles/common/reconciler.go index 7deb259ec..da29a10a7 100644 --- a/controllers/profiles/common/reconciler.go +++ b/controllers/profiles/common/reconciler.go @@ -70,9 +70,9 @@ func NewReconciler(support *StateSupport, stateMachine *ReconciliationStateMachi } // Reconcile does the actual reconciliation algorithm based on a set of ReconciliationState -func (b *Reconciler) Reconcile(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, error) { +func (b *Reconciler) Reconcile(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, error) { workflow.Status.Manager().InitializeConditions() - result, objects, err := b.reconciliationStateMachine.do(ctx, workflow, plf) + result, objects, err := b.reconciliationStateMachine.do(ctx, workflow) if err != nil { return result, err } @@ -97,11 +97,11 @@ type ReconciliationStateMachine struct { states []profiles.ReconciliationState } -func (r *ReconciliationStateMachine) do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { +func (r *ReconciliationStateMachine) do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { for _, h := range r.states { if h.CanReconcile(workflow) { klog.V(log.I).InfoS("Found a condition to reconcile.", "Conditions", workflow.Status.Conditions) - result, objs, err := h.Do(ctx, workflow, plf) + result, objs, err := h.Do(ctx, workflow) if err != nil { return result, objs, err } diff --git a/controllers/profiles/dev/object_creators_dev.go b/controllers/profiles/dev/object_creators_dev.go index 0db4b96d6..63b7b8fe3 100644 --- a/controllers/profiles/dev/object_creators_dev.go +++ b/controllers/profiles/dev/object_creators_dev.go @@ -44,8 +44,8 @@ const ( // aiming a vanilla Kubernetes Deployment. // It maps the default HTTP port (80) to the target Java application webserver on port 8080. // It configures the Service as a NodePort type service, in this way it will be easier for a developer access the service -func serviceCreator(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (client.Object, error) { - object, _ := common.ServiceCreator(workflow, plf) +func serviceCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { + object, _ := common.ServiceCreator(workflow) service := object.(*corev1.Service) // Let's double-check that the workflow is using the Dev Profile we would like to expose it via NodePort if profiles.IsDevProfile(workflow) { @@ -54,8 +54,9 @@ func serviceCreator(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlo return service, nil } -func deploymentCreator(workflow *operatorapi.SonataFlow, platform *operatorapi.SonataFlowPlatform) (client.Object, error) { - obj, err := common.DeploymentCreator(workflow, platform) +func deploymentCreator(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (client.Object, error) { + + obj, err := common.DeploymentCreator(workflow, plf) if err != nil { return nil, err } @@ -74,7 +75,7 @@ func deploymentCreator(workflow *operatorapi.SonataFlow, platform *operatorapi.S } // workflowDefConfigMapCreator creates a new ConfigMap that holds the definition of a workflow specification. -func workflowDefConfigMapCreator(workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (client.Object, error) { +func workflowDefConfigMapCreator(workflow *operatorapi.SonataFlow) (client.Object, error) { configMap, err := workflowdef.CreateNewConfigMap(workflow) if err != nil { return nil, err @@ -100,13 +101,13 @@ func deploymentMutateVisitor(workflow *operatorapi.SonataFlow, plf *operatorapi. } } -func ensureWorkflowDefConfigMapMutator(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) common.MutateVisitor { +func ensureWorkflowDefConfigMapMutator(workflow *operatorapi.SonataFlow) common.MutateVisitor { return func(object client.Object) controllerutil.MutateFn { return func() error { if kubeutil.IsObjectNew(object) { return nil } - original, err := workflowDefConfigMapCreator(workflow, plf) + original, err := workflowDefConfigMapCreator(workflow) if err != nil { return err } diff --git a/controllers/profiles/dev/object_creators_dev_test.go b/controllers/profiles/dev/object_creators_dev_test.go index 0a35499c3..8209940a2 100644 --- a/controllers/profiles/dev/object_creators_dev_test.go +++ b/controllers/profiles/dev/object_creators_dev_test.go @@ -31,7 +31,7 @@ import ( func Test_ensureWorkflowDevServiceIsExposed(t *testing.T) { workflow := test.GetBaseSonataFlowWithDevProfile(t.Name()) //On Kubernetes we want the service exposed in Dev with NodePort - service, _ := serviceCreator(workflow, nil) + service, _ := serviceCreator(workflow) service.SetUID("1") service.SetResourceVersion("1") diff --git a/controllers/profiles/dev/profile_dev.go b/controllers/profiles/dev/profile_dev.go index 31596c1e5..14dc188ce 100644 --- a/controllers/profiles/dev/profile_dev.go +++ b/controllers/profiles/dev/profile_dev.go @@ -75,7 +75,7 @@ func NewProfileReconciler(client client.Client, cfg *rest.Config, recorder recor func newObjectEnsurers(support *common.StateSupport) *objectEnsurers { return &objectEnsurers{ - deployment: common.NewObjectEnsurer(support.C, deploymentCreator), + deployment: common.NewObjectEnsurerWithPlatform(support.C, deploymentCreator), service: common.NewObjectEnsurer(support.C, serviceCreator), network: common.NewNoopObjectEnsurer(), definitionConfigMap: common.NewObjectEnsurer(support.C, workflowDefConfigMapCreator), @@ -86,7 +86,7 @@ func newObjectEnsurers(support *common.StateSupport) *objectEnsurers { func newObjectEnsurersOpenShift(support *common.StateSupport) *objectEnsurers { return &objectEnsurers{ - deployment: common.NewObjectEnsurer(support.C, deploymentCreator), + deployment: common.NewObjectEnsurerWithPlatform(support.C, deploymentCreator), service: common.NewObjectEnsurer(support.C, serviceCreator), network: common.NewObjectEnsurer(support.C, common.OpenShiftRouteCreator), definitionConfigMap: common.NewObjectEnsurer(support.C, workflowDefConfigMapCreator), @@ -108,7 +108,7 @@ func newStatusEnrichersOpenShift(support *common.StateSupport) *statusEnrichers } type objectEnsurers struct { - deployment common.ObjectEnsurer + deployment common.ObjectEnsurerWithPlatform service common.ObjectEnsurer network common.ObjectEnsurer definitionConfigMap common.ObjectEnsurer diff --git a/controllers/profiles/dev/profile_dev_test.go b/controllers/profiles/dev/profile_dev_test.go index d7fd5478f..1c3478ea0 100644 --- a/controllers/profiles/dev/profile_dev_test.go +++ b/controllers/profiles/dev/profile_dev_test.go @@ -60,7 +60,7 @@ func Test_OverrideStartupProbe(t *testing.T) { devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err := devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -71,7 +71,7 @@ func Test_OverrideStartupProbe(t *testing.T) { deployment.Spec.Template.Spec.Containers[0].StartupProbe.FailureThreshold = newThreshold assert.NoError(t, client.Update(context.TODO(), deployment)) // reconcile and fetch from the cluster - result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err = devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) deployment = test.MustGetDeployment(t, client, workflow) @@ -88,14 +88,14 @@ func Test_recoverFromFailureNoDeployment(t *testing.T) { reconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) // we are in failed state and have no objects - result, err := reconciler.Reconcile(context.TODO(), workflow, nil) + result, err := reconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) // the recover state tried to clear the conditions of our workflow, so we can try reconciling it again workflow = test.MustGetWorkflow(t, client, workflowID) assert.True(t, workflow.Status.GetTopLevelCondition().IsUnknown()) - result, err = reconciler.Reconcile(context.TODO(), workflow, nil) + result, err = reconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -109,7 +109,7 @@ func Test_recoverFromFailureNoDeployment(t *testing.T) { assert.NoError(t, err) // the fake client won't update the deployment status condition since we don't have a deployment controller // our state will think that we don't have a deployment available yet, so it will try to reset the pods - result, err = reconciler.Reconcile(context.TODO(), workflow, nil) + result, err = reconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -128,7 +128,7 @@ func Test_newDevProfile(t *testing.T) { devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err := devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -169,7 +169,7 @@ func Test_newDevProfile(t *testing.T) { assert.NoError(t, err) // reconcile again - result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err = devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -196,7 +196,7 @@ func Test_newDevProfile(t *testing.T) { workflow.Status.Manager().MarkTrue(api.RunningConditionType) err = client.Status().Update(context.TODO(), workflow) assert.NoError(t, err) - result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err = devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -209,7 +209,7 @@ func Test_devProfileImageDefaultsNoPlatform(t *testing.T) { client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow).WithStatusSubresource(workflow).Build() devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err := devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -226,7 +226,7 @@ func Test_devProfileWithImageSnapshotOverrideWithPlatform(t *testing.T) { client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, platform).WithStatusSubresource(workflow, platform).Build() devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow, platform) + result, err := devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -243,7 +243,7 @@ func Test_devProfileWithWPlatformWithoutDevBaseImageAndWithBaseImage(t *testing. client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, platform).WithStatusSubresource(workflow, platform).Build() devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err := devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -260,7 +260,7 @@ func Test_devProfileWithPlatformWithoutDevBaseImageAndWithoutBaseImage(t *testin client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, platform).WithStatusSubresource(workflow, platform).Build() devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err := devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -305,7 +305,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { errCreate := client.Create(context.Background(), cmUser) assert.Nil(t, errCreate) - result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err := devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -330,7 +330,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { workflow.Status.Manager().MarkTrue(api.RunningConditionType) err = client.Update(context.TODO(), workflow) assert.NoError(t, err) - result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err = devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -346,7 +346,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { workflow.Status.Manager().MarkTrue(api.RunningConditionType) err = client.Update(context.TODO(), workflow) assert.NoError(t, err) - result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err = devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -362,7 +362,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { workflow.Status.Manager().MarkTrue(api.RunningConditionType) err = client.Status().Update(context.TODO(), workflow) assert.NoError(t, err) - result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err = devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, api.ExternalResourcesNotFoundReason, workflow.Status.GetTopLevelCondition().Reason) @@ -370,7 +370,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T) { // delete the link workflow.Spec.Resources.ConfigMaps = nil assert.NoError(t, client.Update(context.TODO(), workflow)) - result, err = devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err = devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) @@ -393,7 +393,7 @@ func Test_VolumeWithCapitalizedPaths(t *testing.T) { devReconciler := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()) - result, err := devReconciler.Reconcile(context.TODO(), workflow, nil) + result, err := devReconciler.Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result) diff --git a/controllers/profiles/dev/states_dev.go b/controllers/profiles/dev/states_dev.go index 55a4eb5a7..9307c2469 100644 --- a/controllers/profiles/dev/states_dev.go +++ b/controllers/profiles/dev/states_dev.go @@ -34,6 +34,7 @@ import ( "github.com/apache/incubator-kie-kogito-serverless-operator/api" operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08" + "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/platform" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/workflowdef" @@ -58,10 +59,13 @@ func (e *ensureRunningWorkflowState) CanReconcile(workflow *operatorapi.SonataFl return workflow.Status.IsReady() || workflow.Status.GetTopLevelCondition().IsUnknown() || workflow.Status.IsChildObjectsProblem() } -func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { +func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { var objs []client.Object - - flowDefCM, _, err := e.ensurers.definitionConfigMap.Ensure(ctx, workflow, plf, ensureWorkflowDefConfigMapMutator(workflow, plf)) + plf, err := platform.GetActivePlatform(context.TODO(), e.C, workflow.Namespace) + if err != nil { + return ctrl.Result{Requeue: false}, objs, err + } + flowDefCM, _, err := e.ensurers.definitionConfigMap.Ensure(ctx, workflow, ensureWorkflowDefConfigMapMutator(workflow)) if err != nil { return ctrl.Result{Requeue: false}, objs, err } @@ -76,7 +80,7 @@ func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatora if err != nil { return ctrl.Result{Requeue: false}, objs, err } - managedPropsCM, _, err := e.ensurers.managedPropsConfigMap.Ensure(ctx, workflow, pl, common.ManagedPropertiesMutateVisitor(ctx, e.StateSupport.Catalog, workflow, pl, userPropsCM.(*corev1.ConfigMap))) + managedPropsCM, _, err := e.ensurers.managedPropsConfigMap.Ensure(ctx, workflow, plf, common.ManagedPropertiesMutateVisitor(ctx, e.StateSupport.Catalog, workflow, plf, userPropsCM.(*corev1.ConfigMap))) if err != nil { return ctrl.Result{Requeue: false}, objs, err } @@ -100,13 +104,13 @@ func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatora } objs = append(objs, deployment) - service, _, err := e.ensurers.service.Ensure(ctx, workflow, plf, common.ServiceMutateVisitor(workflow)) + service, _, err := e.ensurers.service.Ensure(ctx, workflow, common.ServiceMutateVisitor(workflow)) if err != nil { return ctrl.Result{RequeueAfter: constants.RequeueAfterFailure}, objs, err } objs = append(objs, service) - route, _, err := e.ensurers.network.Ensure(ctx, workflow, plf) + route, _, err := e.ensurers.network.Ensure(ctx, workflow) if err != nil { return ctrl.Result{RequeueAfter: constants.RequeueAfterFailure}, objs, err } @@ -151,7 +155,7 @@ func (f *followWorkflowDeploymentState) CanReconcile(workflow *operatorapi.Sonat return workflow.Status.IsWaitingForDeployment() } -func (f *followWorkflowDeploymentState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { +func (f *followWorkflowDeploymentState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { result, err := common.DeploymentManager(f.C).SyncDeploymentStatus(ctx, workflow) if err != nil { return ctrl.Result{RequeueAfter: constants.RequeueAfterFailure}, nil, err @@ -189,7 +193,7 @@ func (r *recoverFromFailureState) CanReconcile(workflow *operatorapi.SonataFlow) return workflow.Status.GetCondition(api.RunningConditionType).IsFalse() } -func (r *recoverFromFailureState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { +func (r *recoverFromFailureState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { // for now, a very basic attempt to recover by rolling out the deployment deployment := &appsv1.Deployment{} if err := r.C.Get(ctx, client.ObjectKeyFromObject(workflow), deployment); err != nil { diff --git a/controllers/profiles/dev/status_enricher_dev_test.go b/controllers/profiles/dev/status_enricher_dev_test.go index 5da675d47..39e4570df 100644 --- a/controllers/profiles/dev/status_enricher_dev_test.go +++ b/controllers/profiles/dev/status_enricher_dev_test.go @@ -39,7 +39,7 @@ func Test_enrichmentStatusOnK8s(t *testing.T) { workflow := test.GetBaseSonataFlowWithDevProfile(t.Name()) workflow.Namespace = toK8SNamespace(t.Name()) - service, _ := common.ServiceCreator(workflow, nil) + service, _ := common.ServiceCreator(workflow) client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, service).Build() obj, err := statusEnricher(context.TODO(), client, workflow) @@ -55,7 +55,7 @@ func Test_enrichmentStatusOnK8s(t *testing.T) { workflow := test.GetBaseSonataFlowWithDevProfile(t.Name()) workflow.Namespace = t.Name() - service, _ := serviceCreator(workflow, nil) + service, _ := serviceCreator(workflow) client := test.NewSonataFlowClientBuilder().WithRuntimeObjects(workflow, service).Build() _, err := statusEnricher(context.TODO(), client, workflow) assert.Error(t, err) @@ -67,7 +67,7 @@ func Test_enrichmentStatusOnOCP(t *testing.T) { t.Run("verify that the service URL is returned with the default cluster name on default namespace", func(t *testing.T) { workflow := test.GetBaseSonataFlowWithDevProfile(t.Name()) workflow.Namespace = toK8SNamespace(t.Name()) - service, _ := serviceCreator(workflow, nil) + service, _ := serviceCreator(workflow) route := &openshiftv1.Route{} route.Name = workflow.Name route.Namespace = workflow.Namespace diff --git a/controllers/profiles/prod/deployment_handler.go b/controllers/profiles/prod/deployment_handler.go index 3f885e976..4581f2cce 100644 --- a/controllers/profiles/prod/deployment_handler.go +++ b/controllers/profiles/prod/deployment_handler.go @@ -43,8 +43,8 @@ func newDeploymentReconciler(stateSupport *common.StateSupport, ensurer *objectE } } -func (d *deploymentReconciler) reconcile(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (reconcile.Result, []client.Object, error) { - return d.reconcileWithBuiltImage(ctx, workflow, plf, "") +func (d *deploymentReconciler) reconcile(ctx context.Context, workflow *operatorapi.SonataFlow) (reconcile.Result, []client.Object, error) { + return d.reconcileWithBuiltImage(ctx, workflow, "") } func (d *deploymentReconciler) reconcileWithBuiltImage(ctx context.Context, workflow *operatorapi.SonataFlow, image string) (reconcile.Result, []client.Object, error) { @@ -67,7 +67,8 @@ func (d *deploymentReconciler) reconcileWithBuiltImage(ctx context.Context, work d.ensurers.deployment.Ensure( ctx, workflow, - d.getDeploymentMutateVisitors(workflow, image, userPropsCM.(*v1.ConfigMap), managedPropsCM.(*v1.ConfigMap))..., + pl, + d.getDeploymentMutateVisitors(workflow, pl, image, userPropsCM.(*v1.ConfigMap), managedPropsCM.(*v1.ConfigMap))..., ) if err != nil { workflow.Status.Manager().MarkFalse(api.RunningConditionType, api.DeploymentUnavailableReason, "Unable to perform the deploy due to ", err) @@ -75,7 +76,7 @@ func (d *deploymentReconciler) reconcileWithBuiltImage(ctx context.Context, work return reconcile.Result{}, nil, err } - service, _, err := d.ensurers.service.Ensure(ctx, workflow, plf, common.ServiceMutateVisitor(workflow)) + service, _, err := d.ensurers.service.Ensure(ctx, workflow, common.ServiceMutateVisitor(workflow)) if err != nil { workflow.Status.Manager().MarkFalse(api.RunningConditionType, api.DeploymentUnavailableReason, "Unable to make the service available due to ", err) _, err = d.PerformStatusUpdate(ctx, workflow) @@ -111,7 +112,7 @@ func (d *deploymentReconciler) getDeploymentMutateVisitors( userPropsCM *v1.ConfigMap, managedPropsCM *v1.ConfigMap) []common.MutateVisitor { if utils.IsOpenShift() { - return []common.MutateVisitor{common.DeploymentMutateVisitor(workflow), + return []common.MutateVisitor{common.DeploymentMutateVisitor(workflow, plf), mountProdConfigMapsMutateVisitor(workflow, userPropsCM, managedPropsCM), addOpenShiftImageTriggerDeploymentMutateVisitor(workflow, image), common.ImageDeploymentMutateVisitor(workflow, image), diff --git a/controllers/profiles/prod/deployment_handler_test.go b/controllers/profiles/prod/deployment_handler_test.go index d11b7bcc1..a133b9635 100644 --- a/controllers/profiles/prod/deployment_handler_test.go +++ b/controllers/profiles/prod/deployment_handler_test.go @@ -40,7 +40,7 @@ func Test_CheckPodTemplateChangesReflectDeployment(t *testing.T) { stateSupport := fakeReconcilerSupport(client) handler := newDeploymentReconciler(stateSupport, newObjectEnsurers(stateSupport)) - result, objects, err := handler.reconcile(context.TODO(), workflow, emptyPlatform) + result, objects, err := handler.reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotEmpty(t, objects) assert.True(t, result.Requeue) @@ -49,7 +49,7 @@ func Test_CheckPodTemplateChangesReflectDeployment(t *testing.T) { expectedImg := "quay.io/apache/my-new-workflow:1.0.0" workflow.Spec.PodTemplate.Container.Image = expectedImg utilruntime.Must(client.Update(context.TODO(), workflow)) - result, objects, err = handler.reconcile(context.TODO(), workflow, emptyPlatform) + result, objects, err = handler.reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotEmpty(t, objects) assert.True(t, result.Requeue) @@ -73,7 +73,7 @@ func Test_CheckDeploymentRolloutAfterCMChange(t *testing.T) { stateSupport := fakeReconcilerSupport(client) handler := newDeploymentReconciler(stateSupport, newObjectEnsurers(stateSupport)) - result, objects, err := handler.reconcile(context.TODO(), workflow, emptyPlatform) + result, objects, err := handler.reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotEmpty(t, objects) assert.True(t, result.Requeue) @@ -135,7 +135,7 @@ func Test_CheckDeploymentUnchangedAfterCMChangeOtherKeys(t *testing.T) { stateSupport := fakeReconcilerSupport(client) handler := newDeploymentReconciler(stateSupport, newObjectEnsurers(stateSupport)) - result, objects, err := handler.reconcile(context.TODO(), workflow, emptyPlatform) + result, objects, err := handler.reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotEmpty(t, objects) assert.True(t, result.Requeue) diff --git a/controllers/profiles/prod/profile_prod.go b/controllers/profiles/prod/profile_prod.go index 356a2adc8..33932d23d 100644 --- a/controllers/profiles/prod/profile_prod.go +++ b/controllers/profiles/prod/profile_prod.go @@ -52,7 +52,7 @@ const ( // ReconciliationState that needs access to it must include this struct as an attribute and initialize it in the profile builder. // Use newObjectEnsurers to facilitate building this struct type objectEnsurers struct { - deployment common.ObjectEnsurer + deployment common.ObjectEnsurerWithPlatform service common.ObjectEnsurer userPropsConfigMap common.ObjectEnsurer managedPropsConfigMap common.ObjectEnsurerWithPlatform @@ -60,7 +60,7 @@ type objectEnsurers struct { func newObjectEnsurers(support *common.StateSupport) *objectEnsurers { return &objectEnsurers{ - deployment: common.NewObjectEnsurer(support.C, common.DeploymentCreator), + deployment: common.NewObjectEnsurerWithPlatform(support.C, common.DeploymentCreator), service: common.NewObjectEnsurer(support.C, common.ServiceCreator), userPropsConfigMap: common.NewObjectEnsurer(support.C, common.UserPropsConfigMapCreator), managedPropsConfigMap: common.NewObjectEnsurerWithPlatform(support.C, common.ManagedPropsConfigMapCreator), diff --git a/controllers/profiles/prod/profile_prod_test.go b/controllers/profiles/prod/profile_prod_test.go index 181461c09..5ca93a97f 100644 --- a/controllers/profiles/prod/profile_prod_test.go +++ b/controllers/profiles/prod/profile_prod_test.go @@ -49,7 +49,7 @@ func Test_Reconciler_ProdOps(t *testing.T) { client := test.NewSonataFlowClientBuilder(). WithRuntimeObjects(workflow). WithStatusSubresource(workflow, &operatorapi.SonataFlowBuild{}).Build() - result, err := NewProfileForOpsReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) + result, err := NewProfileForOpsReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result.RequeueAfter) @@ -60,7 +60,7 @@ func Test_Reconciler_ProdOps(t *testing.T) { assert.False(t, workflow.Status.IsReady()) // Reconcile again to run the deployment handler - result, err = NewProfileForOpsReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) + result, err = NewProfileForOpsReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) assert.NoError(t, err) // Let's check for the right creation of the workflow (one CM volume, one container with a custom image) @@ -88,7 +88,7 @@ func Test_Reconciler_ProdCustomPod(t *testing.T) { client := test.NewSonataFlowClientBuilder(). WithRuntimeObjects(workflow, build, platform). WithStatusSubresource(workflow, build, platform).Build() - _, err := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) + _, err := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) assert.NoError(t, err) // Let's check for the right creation of the workflow (one CM volume, one container with a custom image) @@ -109,7 +109,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { WithRuntimeObjects(workflow, platform). WithStatusSubresource(workflow, platform, &operatorapi.SonataFlowBuild{}).Build() - result, err := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) + result, err := NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.NotNil(t, result.RequeueAfter) @@ -117,7 +117,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { assert.False(t, workflow.Status.IsReady()) // still building - result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) + result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.Equal(t, requeueWhileWaitForBuild, result.RequeueAfter) assert.True(t, workflow.Status.IsBuildRunningOrUnknown()) @@ -130,7 +130,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { assert.NoError(t, client.Status().Update(context.TODO(), build)) // last reconciliation cycle waiting for build - result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) + result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.Equal(t, requeueWhileWaitForBuild, result.RequeueAfter) assert.False(t, workflow.Status.IsBuildRunningOrUnknown()) @@ -138,7 +138,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { assert.Equal(t, api.WaitingForDeploymentReason, workflow.Status.GetTopLevelCondition().Reason) // now we create the objects - result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) + result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.False(t, workflow.Status.IsBuildRunningOrUnknown()) assert.False(t, workflow.Status.IsReady()) @@ -156,7 +156,7 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { err = client.Status().Update(context.TODO(), deployment) assert.NoError(t, err) - result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow, emptyPlatform) + result, err = NewProfileReconciler(client, &rest.Config{}, test.NewFakeRecorder()).Reconcile(context.TODO(), workflow) assert.NoError(t, err) assert.False(t, workflow.Status.IsBuildRunningOrUnknown()) assert.True(t, workflow.Status.IsReady()) @@ -174,7 +174,7 @@ func Test_deployWorkflowReconciliationHandler_handleObjects(t *testing.T) { StateSupport: fakeReconcilerSupport(client), ensurers: newObjectEnsurers(&common.StateSupport{C: client}), } - result, objects, err := handler.Do(context.TODO(), workflow, emptyPlatform) + result, objects, err := handler.Do(context.TODO(), workflow) assert.Greater(t, result.RequeueAfter, int64(0)) assert.NoError(t, err) assert.NotNil(t, result) @@ -203,7 +203,7 @@ func Test_GenerationAnnotationCheck(t *testing.T) { StateSupport: fakeReconcilerSupport(client), ensurers: newObjectEnsurers(&common.StateSupport{C: client}), } - result, objects, err := handler.Do(context.TODO(), workflow, emptyPlatform) + result, objects, err := handler.Do(context.TODO(), workflow) assert.Greater(t, result.RequeueAfter, int64(time.Second)) assert.NoError(t, err) assert.NotNil(t, result) @@ -222,7 +222,7 @@ func Test_GenerationAnnotationCheck(t *testing.T) { StateSupport: fakeReconcilerSupport(client), ensurers: newObjectEnsurers(&common.StateSupport{C: client}), } - result, objects, err = handler.Do(context.TODO(), workflowChanged, emptyPlatform) + result, objects, err = handler.Do(context.TODO(), workflowChanged) assert.NoError(t, err) // no requeue, no objects since the workflow has changed assert.Equal(t, time.Duration(0), result.RequeueAfter) diff --git a/controllers/profiles/prod/states_prod.go b/controllers/profiles/prod/states_prod.go index 199cbd423..feb30dca6 100644 --- a/controllers/profiles/prod/states_prod.go +++ b/controllers/profiles/prod/states_prod.go @@ -48,7 +48,7 @@ func (h *newBuilderState) CanReconcile(workflow *operatorapi.SonataFlow) bool { workflow.Status.IsBuildFailed() } -func (h *newBuilderState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { +func (h *newBuilderState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { _, err := platform.GetActivePlatform(ctx, h.C, workflow.Namespace) if err != nil { if errors.IsNotFound(err) { @@ -100,7 +100,7 @@ func (h *followBuildStatusState) CanReconcile(workflow *operatorapi.SonataFlow) return workflow.Status.IsBuildRunningOrUnknown() || workflow.Status.IsWaitingForBuild() } -func (h *followBuildStatusState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { +func (h *followBuildStatusState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { // Let's retrieve the build to check the status build, err := builder.NewSonataFlowBuildManager(ctx, h.C).GetOrCreateBuild(workflow) if err != nil { @@ -160,7 +160,7 @@ func (h *deployWithBuildWorkflowState) CanReconcile(workflow *operatorapi.Sonata return workflow.Status.GetCondition(api.BuiltConditionType).IsTrue() } -func (h *deployWithBuildWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { +func (h *deployWithBuildWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { // Guard to avoid errors while getting a new builder manager. // Maybe we can do typed errors in the buildManager and // have something like sonataerr.IsPlatformNotFound(err) instead. @@ -189,7 +189,7 @@ func (h *deployWithBuildWorkflowState) Do(ctx context.Context, workflow *operato } // didn't change, business as usual - return newDeploymentReconciler(h.StateSupport, h.ensurers).reconcileWithBuiltImage(ctx, workflow, plf, build.Status.ImageTag) + return newDeploymentReconciler(h.StateSupport, h.ensurers).reconcileWithBuiltImage(ctx, workflow, build.Status.ImageTag) } func (h *deployWithBuildWorkflowState) PostReconcile(ctx context.Context, workflow *operatorapi.SonataFlow) error { diff --git a/controllers/profiles/prod/states_prod_nobuild.go b/controllers/profiles/prod/states_prod_nobuild.go index bc4d1b787..46449ce1f 100644 --- a/controllers/profiles/prod/states_prod_nobuild.go +++ b/controllers/profiles/prod/states_prod_nobuild.go @@ -36,7 +36,7 @@ func (f *ensureBuildSkipped) CanReconcile(workflow *operatorapi.SonataFlow) bool workflow.Status.GetCondition(api.BuiltConditionType).Reason != api.BuildSkippedReason } -func (f *ensureBuildSkipped) Do(ctx context.Context, workflow *operatorapi.SonataFlow, _ *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { +func (f *ensureBuildSkipped) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { // We skip the build, so let's ensure the status reflect that workflow.Status.Manager().MarkFalse(api.BuiltConditionType, api.BuildSkippedReason, "") if _, err := f.PerformStatusUpdate(ctx, workflow); err != nil { @@ -61,8 +61,8 @@ func (f *followDeployWorkflowState) CanReconcile(workflow *operatorapi.SonataFlo return workflow.Status.GetCondition(api.BuiltConditionType).Reason == api.BuildSkippedReason } -func (f *followDeployWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) { - return newDeploymentReconciler(f.StateSupport, f.ensurers).reconcile(ctx, workflow, plf) +func (f *followDeployWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { + return newDeploymentReconciler(f.StateSupport, f.ensurers).reconcile(ctx, workflow) } func (f *followDeployWorkflowState) PostReconcile(ctx context.Context, workflow *operatorapi.SonataFlow) error { diff --git a/controllers/profiles/profile.go b/controllers/profiles/profile.go index f8c93063d..794ca7a70 100644 --- a/controllers/profiles/profile.go +++ b/controllers/profiles/profile.go @@ -59,7 +59,7 @@ import ( // // While debugging, focus on the ReconciliationState(s), not in the profile implementation since the base algorithm is the same for every profile. type ProfileReconciler interface { - Reconcile(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, error) + Reconcile(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, error) GetProfile() metadata.ProfileType } @@ -69,7 +69,7 @@ type ReconciliationState interface { CanReconcile(workflow *operatorapi.SonataFlow) bool // Do perform the reconciliation task. It returns the controller result, the objects updated, and an error if any. // Objects can be nil if the reconciliation state doesn't perform any updates in any Kubernetes object. - Do(ctx context.Context, workflow *operatorapi.SonataFlow, plf *operatorapi.SonataFlowPlatform) (ctrl.Result, []client.Object, error) + Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) // PostReconcile performs the actions to perform after the reconciliation that are not mandatory PostReconcile(ctx context.Context, workflow *operatorapi.SonataFlow) error } diff --git a/controllers/sonataflow_controller.go b/controllers/sonataflow_controller.go index f946eab41..447e386f6 100644 --- a/controllers/sonataflow_controller.go +++ b/controllers/sonataflow_controller.go @@ -94,11 +94,7 @@ func (r *SonataFlowReconciler) Reconcile(ctx context.Context, req ctrl.Request) klog.V(log.I).InfoS("Ignoring request because resource is not assigned to current operator") return reconcile.Result{}, nil } - plf, err := platform.GetActivePlatform(context.TODO(), r.Client, workflow.Namespace) - if err != nil { - return reconcile.Result{}, err - } - return profiles.NewReconciler(r.Client, r.Config, r.Recorder, workflow).Reconcile(ctx, workflow, plf) + return profiles.NewReconciler(r.Client, r.Config, r.Recorder, workflow).Reconcile(ctx, workflow) } func platformEnqueueRequestsFromMapFunc(c client.Client, p *operatorapi.SonataFlowPlatform) []reconcile.Request { diff --git a/controllers/sonataflowplatform_controller_test.go b/controllers/sonataflowplatform_controller_test.go index c37957ca0..94ad9a8fe 100644 --- a/controllers/sonataflowplatform_controller_test.go +++ b/controllers/sonataflowplatform_controller_test.go @@ -77,7 +77,6 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.Equal(t, "quay.io/kiegroup", ksp.Spec.Build.Config.Registry.Address) assert.Equal(t, "regcred", ksp.Spec.Build.Config.Registry.Secret) assert.Equal(t, v1alpha08.OperatorBuildStrategy, ksp.Spec.Build.Config.BuildStrategy) - assert.Nil(t, ksp.Spec.Services) assert.Equal(t, v1alpha08.PlatformClusterKubernetes, ksp.Status.Cluster) assert.Equal(t, v1alpha08.PlatformCreatingReason, ksp.Status.GetTopLevelCondition().Reason) @@ -87,7 +86,7 @@ func TestSonataFlowPlatformController(t *testing.T) { namespace := t.Name() // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) - ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{}, } @@ -137,7 +136,7 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NotContains(t, dep.Spec.Template.Spec.Containers[0].Env, env) // Check with persistence set - ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistencePlatformSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, }} @@ -166,7 +165,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) var replicas int32 = 2 - ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{ PodTemplate: v1alpha08.PodTemplateSpec{ Replicas: &replicas, @@ -224,7 +223,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set url := "jdbc:postgresql://host:1234/database?currentSchema=data-index-service" - ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistencePlatformSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: url, }} @@ -263,7 +262,7 @@ func TestSonataFlowPlatformController(t *testing.T) { DataIndex: &v1alpha08.ServiceSpec{}, JobService: &v1alpha08.ServiceSpec{}, }, - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, @@ -351,7 +350,7 @@ func TestSonataFlowPlatformController(t *testing.T) { ksp.Spec = v1alpha08.SonataFlowPlatformSpec{ Services: v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{ - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "dataIndex"}, JdbcUrl: urlDI, @@ -359,7 +358,7 @@ func TestSonataFlowPlatformController(t *testing.T) { }, }, JobService: &v1alpha08.ServiceSpec{ - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "job"}, JdbcUrl: urlJS, @@ -367,7 +366,7 @@ func TestSonataFlowPlatformController(t *testing.T) { }, }, }, - Persistence: &v1alpha08.PersistenceSpec{ + Persistence: &v1alpha08.PersistencePlatformSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, @@ -462,7 +461,7 @@ func TestSonataFlowPlatformController(t *testing.T) { namespace := t.Name() // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) - ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ JobService: &v1alpha08.ServiceSpec{}, } @@ -509,7 +508,7 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NotContains(t, dep.Spec.Template.Spec.Containers[0].Env, envDataIndex) // Check with persistence set - ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistencePlatformSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, }} @@ -539,7 +538,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) var replicas int32 = 2 - ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ JobService: &v1alpha08.ServiceSpec{ PodTemplate: v1alpha08.PodTemplateSpec{ Replicas: &replicas, @@ -594,7 +593,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set url := "jdbc:postgresql://host:1234/database?currentSchema=data-index-service" - ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistencePlatformSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: url, }} @@ -620,7 +619,7 @@ func TestSonataFlowPlatformController(t *testing.T) { namespace := t.Name() // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) - ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{}, JobService: &v1alpha08.ServiceSpec{}, } @@ -688,7 +687,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) - ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{}, JobService: &v1alpha08.ServiceSpec{}, } @@ -786,7 +785,7 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.Equal(t, ksp2.Status.ClusterPlatformRef.Services.JobServiceRef.Url, psJs.GetLocalServiceBaseUrl()) assert.Equal(t, psJs.GetLocalServiceBaseUrl()+constants.JobServiceJobEventsPath, psJs2.GetServiceBaseUrl()+constants.JobServiceJobEventsPath) - ksp2.Spec.Services = &v1alpha08.ServicesPlatformSpec{} + ksp2.Spec.Services = v1alpha08.ServicesPlatformSpec{} assert.NoError(t, cl.Update(context.TODO(), ksp2)) _, err = r.Reconcile(context.TODO(), req2) @@ -799,6 +798,5 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NotNil(t, ksp2.Status.ClusterPlatformRef) assert.Equal(t, kscp.Spec.PlatformRef.Name, ksp2.Status.ClusterPlatformRef.PlatformRef.Name) assert.Equal(t, kscp.Spec.PlatformRef.Namespace, ksp2.Status.ClusterPlatformRef.PlatformRef.Namespace) - assert.Nil(t, ksp2.Status.ClusterPlatformRef.Services) }) } diff --git a/operator.yaml b/operator.yaml index f6e912a6b..50870b787 100644 --- a/operator.yaml +++ b/operator.yaml @@ -26861,7 +26861,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: quay.io/jordigilh/kogito-serverless-operator-nightly:latest + image: quay.io/kiegroup/kogito-serverless-operator-nightly:latest livenessProbe: httpGet: path: /healthz diff --git a/test/testdata/platform/services/prod/postgreSQL/02-sonataflow_platform.yaml b/test/testdata/platform/services/prod/postgreSQL/02-sonataflow_platform.yaml index feae7a943..9248f57b7 100644 --- a/test/testdata/platform/services/prod/postgreSQL/02-sonataflow_platform.yaml +++ b/test/testdata/platform/services/prod/postgreSQL/02-sonataflow_platform.yaml @@ -18,10 +18,9 @@ metadata: name: sonataflow-platform spec: build: - template: - buildArgs: - - name: QUARKUS_EXTENSIONS - value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:999-SNAPSHOT + config: + strategyOptions: + KanikoBuildCacheEnabled: "true" services: dataIndex: enabled: true From 77a9134297d4785d70ced225178e8dc670486b10 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Mon, 5 Feb 2024 16:57:49 +0100 Subject: [PATCH 16/22] Updates based on latest changes and revert some unwanted changes that came in by accident Signed-off-by: Jordi Gil --- api/v1alpha08/sonataflow_persistence_types.go | 12 +++++++ api/v1alpha08/sonataflow_types.go | 2 +- .../sonataflowplatform_services_types.go | 2 +- api/v1alpha08/sonataflowplatform_types.go | 13 +------ api/v1alpha08/zz_generated.deepcopy.go | 14 ++++---- .../sonataflow.org_sonataflowplatforms.yaml | 3 ++ .../manifests/sonataflow.org_sonataflows.yaml | 1 + .../sonataflow.org_sonataflowplatforms.yaml | 3 ++ .../crd/bases/sonataflow.org_sonataflows.yaml | 1 + controllers/platform/platformutils.go | 16 +++++---- .../services/properties_services_test.go | 22 ++++++++++-- controllers/platform/services/services.go | 2 +- .../profiles/common/object_creators_test.go | 16 ++++----- .../profiles/common/persistence/postgresql.go | 6 ++-- .../common/properties/application_test.go | 13 +++++-- .../sonataflowplatform_controller_test.go | 34 +++++++++---------- operator.yaml | 4 +++ .../02-sonataflow_platform.yaml | 5 --- .../02-sonataflow_platform.yaml | 4 --- .../from_platform/02-sonataflow_platform.yaml | 4 --- .../02-sonataflow_platform.yaml | 4 --- 21 files changed, 102 insertions(+), 79 deletions(-) diff --git a/api/v1alpha08/sonataflow_persistence_types.go b/api/v1alpha08/sonataflow_persistence_types.go index f93b7a4ca..fa39772cc 100644 --- a/api/v1alpha08/sonataflow_persistence_types.go +++ b/api/v1alpha08/sonataflow_persistence_types.go @@ -14,6 +14,18 @@ package v1alpha08 +// PersistenceOptionsSpec configures the DataBase support for both platform services and workflows. For services, it allows +// configuring a generic database connectivity if the service does not come with its own configured. In case of workflows, +// the operator will add the necessary JDBC properties to in the workflow's application.properties so that it can communicate +// with the persistence service based on the spec provided here. +// +optional +// +kubebuilder:validation:MaxProperties=1 +type PersistenceOptionsSpec struct { + // Connect configured services to a postgresql database. + // +optional + PostgreSQL *PersistencePostgreSQL `json:"postgresql,omitempty"` +} + // PersistencePostgreSQL configure postgresql connection for service(s). // +kubebuilder:validation:MinProperties=2 // +kubebuilder:validation:MaxProperties=2 diff --git a/api/v1alpha08/sonataflow_types.go b/api/v1alpha08/sonataflow_types.go index 644fb0a3a..04aa5440c 100644 --- a/api/v1alpha08/sonataflow_types.go +++ b/api/v1alpha08/sonataflow_types.go @@ -658,7 +658,7 @@ type SonataFlowSpec struct { //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="podTemplate" PodTemplate PodTemplateSpec `json:"podTemplate,omitempty"` // Persistence defines the database persistence configuration for the workflow - Persistence *PersistencePlatformSpec `json:"persistence,omitempty"` + Persistence *PersistenceOptionsSpec `json:"persistence,omitempty"` } // SonataFlowStatus defines the observed state of SonataFlow diff --git a/api/v1alpha08/sonataflowplatform_services_types.go b/api/v1alpha08/sonataflowplatform_services_types.go index ae5e4a99c..a16bc2782 100644 --- a/api/v1alpha08/sonataflowplatform_services_types.go +++ b/api/v1alpha08/sonataflowplatform_services_types.go @@ -32,7 +32,7 @@ type ServiceSpec struct { Enabled *bool `json:"enabled,omitempty"` // Persists service to a datasource of choice. Ephemeral by default. // +optional - Persistence *PersistencePlatformSpec `json:"persistence,omitempty"` + Persistence *PersistenceOptionsSpec `json:"persistence,omitempty"` // PodTemplate describes the deployment details of this platform service instance. //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="podTemplate" PodTemplate PodTemplateSpec `json:"podTemplate,omitempty"` diff --git a/api/v1alpha08/sonataflowplatform_types.go b/api/v1alpha08/sonataflowplatform_types.go index 84e701376..a31fa9dff 100644 --- a/api/v1alpha08/sonataflowplatform_types.go +++ b/api/v1alpha08/sonataflowplatform_types.go @@ -51,18 +51,7 @@ type SonataFlowPlatformSpec struct { // the configuration is used as the persistence for platform services and sonataflow instances // that don't provide one of their own. // +optional - Persistence *PersistencePlatformSpec `json:"persistence,omitempty"` -} - -// PersistencePlatformSpec configures the DataBase support for both platform services and workflows. For services, it allows -// configuring a generic database connectivity if the service does not come with its own configured. In case of workflows, -// the operator will add the necessary JDBC properties to in the workflow's application.properties so that it can communicate -// with the persistence service based on the spec provided here. -// +optional -type PersistencePlatformSpec struct { - // Connect configured services to a postgresql database. - // +optional - PostgreSQL *PersistencePostgreSQL `json:"postgresql,omitempty"` + Persistence *PersistenceOptionsSpec `json:"persistence,omitempty"` } // PlatformCluster is the kind of orchestration cluster the platform is installed into diff --git a/api/v1alpha08/zz_generated.deepcopy.go b/api/v1alpha08/zz_generated.deepcopy.go index 0819ad1f8..fe3b1913c 100644 --- a/api/v1alpha08/zz_generated.deepcopy.go +++ b/api/v1alpha08/zz_generated.deepcopy.go @@ -320,7 +320,7 @@ func (in *Flow) DeepCopy() *Flow { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PersistencePlatformSpec) DeepCopyInto(out *PersistencePlatformSpec) { +func (in *PersistenceOptionsSpec) DeepCopyInto(out *PersistenceOptionsSpec) { *out = *in if in.PostgreSQL != nil { in, out := &in.PostgreSQL, &out.PostgreSQL @@ -329,12 +329,12 @@ func (in *PersistencePlatformSpec) DeepCopyInto(out *PersistencePlatformSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistencePlatformSpec. -func (in *PersistencePlatformSpec) DeepCopy() *PersistencePlatformSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistenceOptionsSpec. +func (in *PersistenceOptionsSpec) DeepCopy() *PersistenceOptionsSpec { if in == nil { return nil } - out := new(PersistencePlatformSpec) + out := new(PersistenceOptionsSpec) in.DeepCopyInto(out) return out } @@ -645,7 +645,7 @@ func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { } if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PersistencePlatformSpec) + *out = new(PersistenceOptionsSpec) (*in).DeepCopyInto(*out) } in.PodTemplate.DeepCopyInto(&out.PodTemplate) @@ -1034,7 +1034,7 @@ func (in *SonataFlowPlatformSpec) DeepCopyInto(out *SonataFlowPlatformSpec) { } if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PersistencePlatformSpec) + *out = new(PersistenceOptionsSpec) (*in).DeepCopyInto(*out) } } @@ -1085,7 +1085,7 @@ func (in *SonataFlowSpec) DeepCopyInto(out *SonataFlowSpec) { in.PodTemplate.DeepCopyInto(&out.PodTemplate) if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PersistencePlatformSpec) + *out = new(PersistenceOptionsSpec) (*in).DeepCopyInto(*out) } } diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml index be9a64a73..3278706b4 100644 --- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml +++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml @@ -425,6 +425,7 @@ spec: When this field is set, the configuration is used as the persistence for platform services and sonataflow instances that don't provide one of their own. + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql database. @@ -498,6 +499,7 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql @@ -8369,6 +8371,7 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql diff --git a/bundle/manifests/sonataflow.org_sonataflows.yaml b/bundle/manifests/sonataflow.org_sonataflows.yaml index 23bd17a76..76c704fb3 100644 --- a/bundle/manifests/sonataflow.org_sonataflows.yaml +++ b/bundle/manifests/sonataflow.org_sonataflows.yaml @@ -2105,6 +2105,7 @@ spec: persistence: description: Persistence defines the database persistence configuration for the workflow + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql database. diff --git a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml index 25e4f505b..b2dd3cc42 100644 --- a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml +++ b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml @@ -426,6 +426,7 @@ spec: When this field is set, the configuration is used as the persistence for platform services and sonataflow instances that don't provide one of their own. + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql database. @@ -499,6 +500,7 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql @@ -8370,6 +8372,7 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql diff --git a/config/crd/bases/sonataflow.org_sonataflows.yaml b/config/crd/bases/sonataflow.org_sonataflows.yaml index 65dd887ff..e5214329e 100644 --- a/config/crd/bases/sonataflow.org_sonataflows.yaml +++ b/config/crd/bases/sonataflow.org_sonataflows.yaml @@ -2106,6 +2106,7 @@ spec: persistence: description: Persistence defines the database persistence configuration for the workflow + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql database. diff --git a/controllers/platform/platformutils.go b/controllers/platform/platformutils.go index 9249bed01..ae14c40e9 100644 --- a/controllers/platform/platformutils.go +++ b/controllers/platform/platformutils.go @@ -111,13 +111,15 @@ func setPlatformDefaults(p *operatorapi.SonataFlowPlatform, verbose bool) error } // When dataIndex object set, default to enabled if bool not set - var enable = true - if p.Spec.Services.DataIndex != nil && p.Spec.Services.DataIndex.Enabled == nil { - p.Spec.Services.DataIndex.Enabled = &enable - } - // When the JobService field has a value, default to enabled if the `Enabled` field's value is nil - if p.Spec.Services.JobService != nil && p.Spec.Services.JobService.Enabled == nil { - p.Spec.Services.JobService.Enabled = &enable + if p.Spec.Services != nil { + var enable = true + if p.Spec.Services.DataIndex != nil && p.Spec.Services.DataIndex.Enabled == nil { + p.Spec.Services.DataIndex.Enabled = &enable + } + // When the JobService field has a value, default to enabled if the `Enabled` field's value is nil + if p.Spec.Services.JobService != nil && p.Spec.Services.JobService.Enabled == nil { + p.Spec.Services.JobService.Enabled = &enable + } } setStatusAdditionalInfo(p) diff --git a/controllers/platform/services/properties_services_test.go b/controllers/platform/services/properties_services_test.go index c0983b0b7..46ea31a44 100644 --- a/controllers/platform/services/properties_services_test.go +++ b/controllers/platform/services/properties_services_test.go @@ -173,6 +173,9 @@ func generatePlatform(opts ...plfmOptionFn) *operatorapi.SonataFlowPlatform { func setJobServiceEnabledValue(v *bool) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { + if p.Spec.Services == nil { + p.Spec.Services = &operatorapi.ServicesPlatformSpec{} + } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } @@ -182,6 +185,9 @@ func setJobServiceEnabledValue(v *bool) plfmOptionFn { func setDataIndexEnabledValue(v *bool) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { + if p.Spec.Services == nil { + p.Spec.Services = &operatorapi.ServicesPlatformSpec{} + } if p.Spec.Services.DataIndex == nil { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } @@ -191,6 +197,9 @@ func setDataIndexEnabledValue(v *bool) plfmOptionFn { func emptyDataIndexServiceSpec() plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { + if p.Spec.Services == nil { + p.Spec.Services = &operatorapi.ServicesPlatformSpec{} + } if p.Spec.Services.DataIndex == nil { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } @@ -199,6 +208,9 @@ func emptyDataIndexServiceSpec() plfmOptionFn { func emptyJobServiceSpec() plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { + if p.Spec.Services == nil { + p.Spec.Services = &operatorapi.ServicesPlatformSpec{} + } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } @@ -219,11 +231,14 @@ func setPlatformName(name string) plfmOptionFn { func setJobServiceJDBC(jdbc string) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { + if p.Spec.Services == nil { + p.Spec.Services = &operatorapi.ServicesPlatformSpec{} + } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } if p.Spec.Services.JobService.Persistence == nil { - p.Spec.Services.JobService.Persistence = &operatorapi.PersistencePlatformSpec{} + p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceOptionsSpec{} } if p.Spec.Services.JobService.Persistence.PostgreSQL == nil { p.Spec.Services.JobService.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} @@ -234,11 +249,14 @@ func setJobServiceJDBC(jdbc string) plfmOptionFn { func setDataIndexJDBC(jdbc string) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { + if p.Spec.Services == nil { + p.Spec.Services = &operatorapi.ServicesPlatformSpec{} + } if p.Spec.Services.DataIndex == nil { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } if p.Spec.Services.DataIndex.Persistence == nil { - p.Spec.Services.DataIndex.Persistence = &operatorapi.PersistencePlatformSpec{} + p.Spec.Services.DataIndex.Persistence = &operatorapi.PersistenceOptionsSpec{} } if p.Spec.Services.DataIndex.Persistence.PostgreSQL == nil { p.Spec.Services.DataIndex.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} diff --git a/controllers/platform/services/services.go b/controllers/platform/services/services.go index d75a5a6bb..b5e25d264 100644 --- a/controllers/platform/services/services.go +++ b/controllers/platform/services/services.go @@ -459,7 +459,7 @@ func isJobServiceSet(platform *operatorapi.SonataFlowPlatform) bool { } func isServicesSet(platform *operatorapi.SonataFlowPlatform) bool { - return platform != nil && (platform.Spec.Services.JobService != nil || platform.Spec.Services.DataIndex != nil) + return platform != nil && platform.Spec.Services != nil } func generateServiceURL(protocol string, namespace string, name string) string { diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index 9e1a29b4d..52b3160be 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -214,7 +214,7 @@ func TestMergePodSpec_WithPostgreSQL_and_JDBC_URL_field(t *testing.T) { }, }, }, - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: "jdbc:postgresql://host:port/database?currentSchema=workflow", @@ -304,7 +304,7 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_In_Workflow_CR(t *testin }, }, }, - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ @@ -375,7 +375,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti Namespace: "default", }, Spec: v1alpha08.SonataFlowPlatformSpec{ - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", @@ -396,7 +396,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ - Persistence: &v1alpha08.PersistencePlatformSpec{}, + Persistence: &v1alpha08.PersistenceOptionsSpec{}, } object, err := DeploymentCreator(workflow, p) assert.NoError(t, err) @@ -456,7 +456,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t Namespace: "default", }, Spec: v1alpha08.SonataFlowPlatformSpec{ - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", @@ -493,7 +493,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t }, }, }, - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ @@ -563,7 +563,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Req Namespace: "default", }, Spec: v1alpha08.SonataFlowPlatformSpec{ - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", @@ -605,7 +605,7 @@ func TestMergePodSpec_WithEphemeralPostgreSQL_And_Undefined_PostgreSQL_Image_In_ } workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ - Persistence: &v1alpha08.PersistencePlatformSpec{}, + Persistence: &v1alpha08.PersistenceOptionsSpec{}, } _, err := DeploymentCreator(workflow, p) assert.Error(t, err) diff --git a/controllers/profiles/common/persistence/postgresql.go b/controllers/profiles/common/persistence/postgresql.go index 3ab969634..fca5ed82e 100644 --- a/controllers/profiles/common/persistence/postgresql.go +++ b/controllers/profiles/common/persistence/postgresql.go @@ -24,9 +24,7 @@ import ( ) const ( - defaultSchemaName = "default" - defaultDatabaseName = "sonataflow" - + defaultDatabaseName = "sonataflow" timeoutSeconds = 3 failureThreshold = 5 initialPeriodSeconds = 15 @@ -114,7 +112,7 @@ func ConfigurePostgreSQLEnv(postgresql *operatorapi.PersistencePostgreSQL, datab } } -func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistencePlatformSpec, defaultSchema, namespace string) (*corev1.Container, error) { +func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistenceOptionsSpec, defaultSchema, namespace string) (*corev1.Container, error) { if config == nil { return nil, fmt.Errorf("no persistence specification found") } diff --git a/controllers/profiles/common/properties/application_test.go b/controllers/profiles/common/properties/application_test.go index e3780930f..e686aaac6 100644 --- a/controllers/profiles/common/properties/application_test.go +++ b/controllers/profiles/common/properties/application_test.go @@ -203,7 +203,7 @@ func Test_appPropertyHandler_WithServicesWithUserOverrides(t *testing.T) { platform := test.GetBasePlatform() platform.Namespace = ns platform.Spec = operatorapi.SonataFlowPlatformSpec{ - Services: operatorapi.ServicesPlatformSpec{ + Services: &operatorapi.ServicesPlatformSpec{ DataIndex: &operatorapi.ServiceSpec{ Enabled: &enabled, }, @@ -625,6 +625,9 @@ func generatePlatform(opts ...plfmOptionFn) *operatorapi.SonataFlowPlatform { func setJobServiceEnabledValue(v *bool) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { + if p.Spec.Services == nil { + p.Spec.Services = &operatorapi.ServicesPlatformSpec{} + } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } @@ -634,6 +637,9 @@ func setJobServiceEnabledValue(v *bool) plfmOptionFn { func setDataIndexEnabledValue(v *bool) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { + if p.Spec.Services == nil { + p.Spec.Services = &operatorapi.ServicesPlatformSpec{} + } if p.Spec.Services.DataIndex == nil { p.Spec.Services.DataIndex = &operatorapi.ServiceSpec{} } @@ -655,11 +661,14 @@ func setPlatformName(name string) plfmOptionFn { func setJobServiceJDBC(jdbc string) plfmOptionFn { return func(p *operatorapi.SonataFlowPlatform) { + if p.Spec.Services == nil { + p.Spec.Services = &operatorapi.ServicesPlatformSpec{} + } if p.Spec.Services.JobService == nil { p.Spec.Services.JobService = &operatorapi.ServiceSpec{} } if p.Spec.Services.JobService.Persistence == nil { - p.Spec.Services.JobService.Persistence = &operatorapi.PersistencePlatformSpec{} + p.Spec.Services.JobService.Persistence = &operatorapi.PersistenceOptionsSpec{} } if p.Spec.Services.JobService.Persistence.PostgreSQL == nil { p.Spec.Services.JobService.Persistence.PostgreSQL = &operatorapi.PersistencePostgreSQL{} diff --git a/controllers/sonataflowplatform_controller_test.go b/controllers/sonataflowplatform_controller_test.go index 94ad9a8fe..855041e61 100644 --- a/controllers/sonataflowplatform_controller_test.go +++ b/controllers/sonataflowplatform_controller_test.go @@ -86,7 +86,7 @@ func TestSonataFlowPlatformController(t *testing.T) { namespace := t.Name() // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) - ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{}, } @@ -136,7 +136,7 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NotContains(t, dep.Spec.Template.Spec.Containers[0].Env, env) // Check with persistence set - ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistencePlatformSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceOptionsSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, }} @@ -165,7 +165,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) var replicas int32 = 2 - ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{ PodTemplate: v1alpha08.PodTemplateSpec{ Replicas: &replicas, @@ -223,7 +223,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set url := "jdbc:postgresql://host:1234/database?currentSchema=data-index-service" - ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistencePlatformSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceOptionsSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: url, }} @@ -258,11 +258,11 @@ func TestSonataFlowPlatformController(t *testing.T) { ksp := test.GetBasePlatformInReadyPhase(namespace) // Check with persistence set ksp.Spec = v1alpha08.SonataFlowPlatformSpec{ - Services: v1alpha08.ServicesPlatformSpec{ + Services: &v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{}, JobService: &v1alpha08.ServiceSpec{}, }, - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, @@ -348,9 +348,9 @@ func TestSonataFlowPlatformController(t *testing.T) { urlDI := "jdbc:postgresql://localhost:5432/database?currentSchema=data-index-service" urlJS := "jdbc:postgresql://localhost:5432/database?currentSchema=job-service" ksp.Spec = v1alpha08.SonataFlowPlatformSpec{ - Services: v1alpha08.ServicesPlatformSpec{ + Services: &v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{ - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "dataIndex"}, JdbcUrl: urlDI, @@ -358,7 +358,7 @@ func TestSonataFlowPlatformController(t *testing.T) { }, }, JobService: &v1alpha08.ServiceSpec{ - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "job"}, JdbcUrl: urlJS, @@ -366,7 +366,7 @@ func TestSonataFlowPlatformController(t *testing.T) { }, }, }, - Persistence: &v1alpha08.PersistencePlatformSpec{ + Persistence: &v1alpha08.PersistenceOptionsSpec{ PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, @@ -461,7 +461,7 @@ func TestSonataFlowPlatformController(t *testing.T) { namespace := t.Name() // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) - ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ JobService: &v1alpha08.ServiceSpec{}, } @@ -508,7 +508,7 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NotContains(t, dep.Spec.Template.Spec.Containers[0].Env, envDataIndex) // Check with persistence set - ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistencePlatformSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceOptionsSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, }} @@ -538,7 +538,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) var replicas int32 = 2 - ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ JobService: &v1alpha08.ServiceSpec{ PodTemplate: v1alpha08.PodTemplateSpec{ Replicas: &replicas, @@ -593,7 +593,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set url := "jdbc:postgresql://host:1234/database?currentSchema=data-index-service" - ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistencePlatformSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceOptionsSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, JdbcUrl: url, }} @@ -619,7 +619,7 @@ func TestSonataFlowPlatformController(t *testing.T) { namespace := t.Name() // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) - ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{}, JobService: &v1alpha08.ServiceSpec{}, } @@ -687,7 +687,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Create a SonataFlowPlatform object with metadata and spec. ksp := test.GetBasePlatformInReadyPhase(namespace) - ksp.Spec.Services = v1alpha08.ServicesPlatformSpec{ + ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{ DataIndex: &v1alpha08.ServiceSpec{}, JobService: &v1alpha08.ServiceSpec{}, } @@ -785,7 +785,7 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.Equal(t, ksp2.Status.ClusterPlatformRef.Services.JobServiceRef.Url, psJs.GetLocalServiceBaseUrl()) assert.Equal(t, psJs.GetLocalServiceBaseUrl()+constants.JobServiceJobEventsPath, psJs2.GetServiceBaseUrl()+constants.JobServiceJobEventsPath) - ksp2.Spec.Services = v1alpha08.ServicesPlatformSpec{} + ksp2.Spec.Services = &v1alpha08.ServicesPlatformSpec{} assert.NoError(t, cl.Update(context.TODO(), ksp2)) _, err = r.Reconcile(context.TODO(), req2) diff --git a/operator.yaml b/operator.yaml index 50870b787..9f6302aaa 100644 --- a/operator.yaml +++ b/operator.yaml @@ -904,6 +904,7 @@ spec: When this field is set, the configuration is used as the persistence for platform services and sonataflow instances that don't provide one of their own. + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql database. @@ -977,6 +978,7 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql @@ -8848,6 +8850,7 @@ spec: persistence: description: Persists service to a datasource of choice. Ephemeral by default. + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql @@ -18920,6 +18923,7 @@ spec: persistence: description: Persistence defines the database persistence configuration for the workflow + maxProperties: 1 properties: postgresql: description: Connect configured services to a postgresql database. diff --git a/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml index 0204e50f0..d746ac94b 100644 --- a/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml +++ b/test/testdata/platform/persistence/generic_from_platform_cr/02-sonataflow_platform.yaml @@ -17,11 +17,6 @@ kind: SonataFlowPlatform metadata: name: sonataflow-platform spec: - build: - template: - buildArgs: - - name: QUARKUS_EXTENSIONS - value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:2.0.0-SNAPSHOT persistence: postgresql: secretRef: diff --git a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml index a8013e600..90e361b86 100644 --- a/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml +++ b/test/testdata/platform/persistence/overwritten_by_services/02-sonataflow_platform.yaml @@ -18,10 +18,6 @@ metadata: name: sonataflow-platform spec: build: - template: - buildArgs: - - name: QUARKUS_EXTENSION - value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:2.0.0-SNAPSHOT config: strategyOptions: KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml index f4cb459db..9debf41df 100644 --- a/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml @@ -29,10 +29,6 @@ spec: databaseName: sonataflow databaseSchema: myschema build: - template: - buildArgs: - - name: QUARKUS_EXTENSIONS - value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:999-SNAPSHOT,org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final config: strategyOptions: KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml index 102195889..6aada1b6d 100644 --- a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml @@ -29,10 +29,6 @@ spec: databaseName: db_name databaseSchema: myschema build: - template: - buildArgs: - - name: QUARKUS_EXTENSIONS - value: org.kie.kogito:kogito-addons-quarkus-jobs-knative-eventing:999-SNAPSHOT,org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final config: strategyOptions: KanikoBuildCacheEnabled: "true" From f98eb50c9d0423df396120c0d533e84eebc2ff30 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Tue, 6 Feb 2024 09:33:34 +0100 Subject: [PATCH 17/22] Add additional check for nil values in platform spec and service spec before processing the postgreSQL configuration Signed-off-by: Jordi Gil --- controllers/platform/services/services.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/controllers/platform/services/services.go b/controllers/platform/services/services.go index b5e25d264..90096b0f1 100644 --- a/controllers/platform/services/services.go +++ b/controllers/platform/services/services.go @@ -200,8 +200,9 @@ func (d DataIndexHandler) MergePodSpec(podSpec corev1.PodSpec) (corev1.PodSpec, // hasPostgreSQLConfigured returns true when either the SonataFlow Platform PostgreSQL CR's structure or the one in the Data Index service specification is not nil func (d DataIndexHandler) hasPostgreSQLConfigured() bool { - return (d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSQL != nil) || - (d.platform.Spec.Persistence != nil && d.platform.Spec.Persistence.PostgreSQL != nil) + return d.IsServiceSetInSpec() && + ((d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSQL != nil) || + (d.platform.Spec.Persistence != nil && d.platform.Spec.Persistence.PostgreSQL != nil)) } func (d DataIndexHandler) ConfigurePersistence(containerSpec *corev1.Container) *corev1.Container { @@ -373,8 +374,9 @@ func (j JobServiceHandler) MergeContainerSpec(containerSpec *corev1.Container) ( // hasPostgreSQLConfigured returns true when either the SonataFlow Platform PostgreSQL CR's structure or the one in the Job service specification is not nil func (j JobServiceHandler) hasPostgreSQLConfigured() bool { - return (j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSQL != nil) || - (j.platform.Spec.Persistence != nil && j.platform.Spec.Persistence.PostgreSQL != nil) + return j.IsServiceSetInSpec() && + ((j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSQL != nil) || + (j.platform.Spec.Persistence != nil && j.platform.Spec.Persistence.PostgreSQL != nil)) } func (j JobServiceHandler) ConfigurePersistence(containerSpec *corev1.Container) *corev1.Container { From e69129c063b2573ebc81634afc60bb4c02d6fed8 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Wed, 7 Feb 2024 11:14:30 +0100 Subject: [PATCH 18/22] Revert unnecessary change in variable name Signed-off-by: Jordi Gil --- controllers/profiles/dev/states_dev.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/controllers/profiles/dev/states_dev.go b/controllers/profiles/dev/states_dev.go index 9307c2469..e186a3589 100644 --- a/controllers/profiles/dev/states_dev.go +++ b/controllers/profiles/dev/states_dev.go @@ -61,10 +61,7 @@ func (e *ensureRunningWorkflowState) CanReconcile(workflow *operatorapi.SonataFl func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { var objs []client.Object - plf, err := platform.GetActivePlatform(context.TODO(), e.C, workflow.Namespace) - if err != nil { - return ctrl.Result{Requeue: false}, objs, err - } + flowDefCM, _, err := e.ensurers.definitionConfigMap.Ensure(ctx, workflow, ensureWorkflowDefConfigMapMutator(workflow)) if err != nil { return ctrl.Result{Requeue: false}, objs, err @@ -73,14 +70,18 @@ func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatora devBaseContainerImage := workflowdef.GetDefaultWorkflowDevModeImageTag() // check if the Platform available - if plf != nil && len(plf.Spec.DevMode.BaseImage) > 0 { - devBaseContainerImage = plf.Spec.DevMode.BaseImage + pl, err := platform.GetActivePlatform(context.TODO(), e.C, workflow.Namespace) + if err != nil { + return ctrl.Result{Requeue: false}, objs, err + } + if pl != nil && len(pl.Spec.DevMode.BaseImage) > 0 { + devBaseContainerImage = pl.Spec.DevMode.BaseImage } userPropsCM, _, err := e.ensurers.userPropsConfigMap.Ensure(ctx, workflow) if err != nil { return ctrl.Result{Requeue: false}, objs, err } - managedPropsCM, _, err := e.ensurers.managedPropsConfigMap.Ensure(ctx, workflow, plf, common.ManagedPropertiesMutateVisitor(ctx, e.StateSupport.Catalog, workflow, plf, userPropsCM.(*corev1.ConfigMap))) + managedPropsCM, _, err := e.ensurers.managedPropsConfigMap.Ensure(ctx, workflow, pl, common.ManagedPropertiesMutateVisitor(ctx, e.StateSupport.Catalog, workflow, pl, userPropsCM.(*corev1.ConfigMap))) if err != nil { return ctrl.Result{Requeue: false}, objs, err } @@ -95,8 +96,8 @@ func (e *ensureRunningWorkflowState) Do(ctx context.Context, workflow *operatora return ctrl.Result{RequeueAfter: constants.RequeueAfterFailure}, objs, nil } - deployment, _, err := e.ensurers.deployment.Ensure(ctx, workflow, plf, - deploymentMutateVisitor(workflow, plf), + deployment, _, err := e.ensurers.deployment.Ensure(ctx, workflow, pl, + deploymentMutateVisitor(workflow, pl), common.ImageDeploymentMutateVisitor(workflow, devBaseContainerImage), mountDevConfigMapsMutateVisitor(workflow, flowDefCM.(*corev1.ConfigMap), userPropsCM.(*corev1.ConfigMap), managedPropsCM.(*corev1.ConfigMap), externalCM)) if err != nil { From 216f826507404260a660a6a0d4ca8a338b332fb8 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Fri, 9 Feb 2024 00:10:02 +0100 Subject: [PATCH 19/22] Change platform persistence to remove database schema and update code in workflow so that when deriving the persistence from the platform, to generate the schema using the workflow's name Signed-off-by: Jordi Gil --- api/v1alpha08/sonataflow_persistence_types.go | 34 +++++++- api/v1alpha08/sonataflowplatform_types.go | 2 +- api/v1alpha08/zz_generated.deepcopy.go | 71 +++++++++++++-- .../sonataflow.org_sonataflowplatforms.yaml | 4 - .../sonataflow.org_sonataflowplatforms.yaml | 4 - .../platform/services/properties_test.go | 12 +++ controllers/platform/services/services.go | 30 ++++++- .../profiles/common/object_creators.go | 15 +++- .../profiles/common/object_creators_test.go | 59 ++++++------- .../profiles/common/persistence/postgresql.go | 3 - .../sonataflowplatform_controller_test.go | 22 +++-- operator.yaml | 4 - test/e2e/workflow_test.go | 3 +- .../ephemeral/02-sonataflow_platform.yaml | 1 - .../02-sonataflow_platform.yaml | 1 - .../01-postgres.yaml | 0 .../02-sonataflow_platform.yaml | 51 +++++++++++ ...3-sonataflow_callbackstatetimeouts.sw.yaml | 0 .../kustomization.yaml | 0 .../01-postgres.yaml | 86 +++++++++++++++++++ .../02-sonataflow_platform.yaml | 1 - ...3-sonataflow_callbackstatetimeouts.sw.yaml | 86 +++++++++++++++++++ .../kustomization.yaml | 33 +++++++ 23 files changed, 452 insertions(+), 70 deletions(-) rename test/testdata/workflow/persistence/{from_platform => from_platform_with_di_and_js_services}/01-postgres.yaml (100%) create mode 100644 test/testdata/workflow/persistence/from_platform_with_di_and_js_services/02-sonataflow_platform.yaml rename test/testdata/workflow/persistence/{from_platform => from_platform_with_di_and_js_services}/03-sonataflow_callbackstatetimeouts.sw.yaml (100%) rename test/testdata/workflow/persistence/{from_platform => from_platform_with_di_and_js_services}/kustomization.yaml (100%) create mode 100644 test/testdata/workflow/persistence/from_platform_without_di_and_js_services/01-postgres.yaml rename test/testdata/workflow/persistence/{from_platform => from_platform_without_di_and_js_services}/02-sonataflow_platform.yaml (96%) create mode 100644 test/testdata/workflow/persistence/from_platform_without_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml create mode 100644 test/testdata/workflow/persistence/from_platform_without_di_and_js_services/kustomization.yaml diff --git a/api/v1alpha08/sonataflow_persistence_types.go b/api/v1alpha08/sonataflow_persistence_types.go index fa39772cc..d0b08e467 100644 --- a/api/v1alpha08/sonataflow_persistence_types.go +++ b/api/v1alpha08/sonataflow_persistence_types.go @@ -14,6 +14,32 @@ package v1alpha08 +// PlatformPersistenceOptionsSpec configures the DataBase in the platform spec. This specification can +// be used by workflows and platform services when they don't provide one of their own. +// +optional +// +kubebuilder:validation:MaxProperties=1 +type PlatformPersistenceOptionsSpec struct { + // Connect configured services to a postgresql database. + // +optional + PostgreSQL *PlatformPersistencePostgreSQL `json:"postgresql,omitempty"` +} + +// PlatformPersistencePostgreSQL configure postgresql connection in a platform to be shared +// by platform services and workflows when required. +// +kubebuilder:validation:MinProperties=2 +// +kubebuilder:validation:MaxProperties=2 +type PlatformPersistencePostgreSQL struct { + // Secret reference to the database user credentials + SecretRef PostgreSQLSecretOptions `json:"secretRef"` + // Service reference to postgresql datasource. Mutually exclusive to jdbcUrl. + // +optional + ServiceRef *SQLServiceOptions `json:"serviceRef,omitempty"` + // PostgreSql JDBC URL. Mutually exclusive to serviceRef. + // e.g. "jdbc:postgresql://host:port/database?currentSchema=data-index-service" + // +optional + JdbcUrl string `json:"jdbcUrl,omitempty"` +} + // PersistenceOptionsSpec configures the DataBase support for both platform services and workflows. For services, it allows // configuring a generic database connectivity if the service does not come with its own configured. In case of workflows, // the operator will add the necessary JDBC properties to in the workflow's application.properties so that it can communicate @@ -53,8 +79,7 @@ type PostgreSQLSecretOptions struct { PasswordKey string `json:"passwordKey,omitempty"` } -// PostgreSQLServiceOptions use k8s service to configure postgresql jdbc url. -type PostgreSQLServiceOptions struct { +type SQLServiceOptions struct { // Name of the postgresql k8s service. Name string `json:"name"` // Namespace of the postgresql k8s service. Defaults to the SonataFlowPlatform's local namespace. @@ -66,6 +91,11 @@ type PostgreSQLServiceOptions struct { // Name of postgresql database to be used. Defaults to "sonataflow" // +optional DatabaseName string `json:"databaseName,omitempty"` +} + +// PostgreSQLServiceOptions use k8s service to configure postgresql jdbc url. +type PostgreSQLServiceOptions struct { + *SQLServiceOptions `json:",inline"` // Schema of postgresql database to be used. Defaults to "data-index-service" // +optional DatabaseSchema string `json:"databaseSchema,omitempty"` diff --git a/api/v1alpha08/sonataflowplatform_types.go b/api/v1alpha08/sonataflowplatform_types.go index a31fa9dff..c702c149b 100644 --- a/api/v1alpha08/sonataflowplatform_types.go +++ b/api/v1alpha08/sonataflowplatform_types.go @@ -51,7 +51,7 @@ type SonataFlowPlatformSpec struct { // the configuration is used as the persistence for platform services and sonataflow instances // that don't provide one of their own. // +optional - Persistence *PersistenceOptionsSpec `json:"persistence,omitempty"` + Persistence *PlatformPersistenceOptionsSpec `json:"persistence,omitempty"` } // PlatformCluster is the kind of orchestration cluster the platform is installed into diff --git a/api/v1alpha08/zz_generated.deepcopy.go b/api/v1alpha08/zz_generated.deepcopy.go index fe3b1913c..cc7870ae5 100644 --- a/api/v1alpha08/zz_generated.deepcopy.go +++ b/api/v1alpha08/zz_generated.deepcopy.go @@ -360,6 +360,47 @@ func (in *PersistencePostgreSQL) DeepCopy() *PersistencePostgreSQL { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlatformPersistenceOptionsSpec) DeepCopyInto(out *PlatformPersistenceOptionsSpec) { + *out = *in + if in.PostgreSQL != nil { + in, out := &in.PostgreSQL, &out.PostgreSQL + *out = new(PlatformPersistencePostgreSQL) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlatformPersistenceOptionsSpec. +func (in *PlatformPersistenceOptionsSpec) DeepCopy() *PlatformPersistenceOptionsSpec { + if in == nil { + return nil + } + out := new(PlatformPersistenceOptionsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlatformPersistencePostgreSQL) DeepCopyInto(out *PlatformPersistencePostgreSQL) { + *out = *in + out.SecretRef = in.SecretRef + if in.ServiceRef != nil { + in, out := &in.ServiceRef, &out.ServiceRef + *out = new(SQLServiceOptions) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlatformPersistencePostgreSQL. +func (in *PlatformPersistencePostgreSQL) DeepCopy() *PlatformPersistencePostgreSQL { + if in == nil { + return nil + } + out := new(PlatformPersistencePostgreSQL) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PlatformServiceRefStatus) DeepCopyInto(out *PlatformServiceRefStatus) { *out = *in @@ -603,10 +644,10 @@ func (in *PostgreSQLSecretOptions) DeepCopy() *PostgreSQLSecretOptions { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PostgreSQLServiceOptions) DeepCopyInto(out *PostgreSQLServiceOptions) { *out = *in - if in.Port != nil { - in, out := &in.Port, &out.Port - *out = new(int) - **out = **in + if in.SQLServiceOptions != nil { + in, out := &in.SQLServiceOptions, &out.SQLServiceOptions + *out = new(SQLServiceOptions) + (*in).DeepCopyInto(*out) } } @@ -635,6 +676,26 @@ func (in *RegistrySpec) DeepCopy() *RegistrySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SQLServiceOptions) DeepCopyInto(out *SQLServiceOptions) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SQLServiceOptions. +func (in *SQLServiceOptions) DeepCopy() *SQLServiceOptions { + if in == nil { + return nil + } + out := new(SQLServiceOptions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { *out = *in @@ -1034,7 +1095,7 @@ func (in *SonataFlowPlatformSpec) DeepCopyInto(out *SonataFlowPlatformSpec) { } if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence - *out = new(PersistenceOptionsSpec) + *out = new(PlatformPersistenceOptionsSpec) (*in).DeepCopyInto(*out) } } diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml index 3278706b4..1752ac7c2 100644 --- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml +++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml @@ -459,10 +459,6 @@ spec: 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 diff --git a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml index b2dd3cc42..a1cb0213b 100644 --- a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml +++ b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml @@ -460,10 +460,6 @@ spec: 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 diff --git a/controllers/platform/services/properties_test.go b/controllers/platform/services/properties_test.go index 7697cb985..828f11e76 100644 --- a/controllers/platform/services/properties_test.go +++ b/controllers/platform/services/properties_test.go @@ -93,6 +93,9 @@ func setServiceName(svcName string) optionFn { if o.ServiceRef == nil { o.ServiceRef = &operatorapi.PostgreSQLServiceOptions{} } + if o.ServiceRef.SQLServiceOptions == nil { + o.ServiceRef.SQLServiceOptions = &operatorapi.SQLServiceOptions{} + } o.ServiceRef.Name = svcName } } @@ -111,6 +114,9 @@ func setDatabaseName(dbName string) optionFn { if o.ServiceRef == nil { o.ServiceRef = &operatorapi.PostgreSQLServiceOptions{} } + if o.ServiceRef.SQLServiceOptions == nil { + o.ServiceRef.SQLServiceOptions = &operatorapi.SQLServiceOptions{} + } o.ServiceRef.DatabaseName = dbName } } @@ -120,6 +126,9 @@ func setServiceNamespace(svcNamespace string) optionFn { if o.ServiceRef == nil { o.ServiceRef = &operatorapi.PostgreSQLServiceOptions{} } + if o.ServiceRef.SQLServiceOptions == nil { + o.ServiceRef.SQLServiceOptions = &operatorapi.SQLServiceOptions{} + } o.ServiceRef.Namespace = svcNamespace } } @@ -129,6 +138,9 @@ func setDBPort(portNumber int) optionFn { if o.ServiceRef == nil { o.ServiceRef = &operatorapi.PostgreSQLServiceOptions{} } + if o.ServiceRef.SQLServiceOptions == nil { + o.ServiceRef.SQLServiceOptions = &operatorapi.SQLServiceOptions{} + } o.ServiceRef.Port = &portNumber } } diff --git a/controllers/platform/services/services.go b/controllers/platform/services/services.go index 90096b0f1..77ef1075d 100644 --- a/controllers/platform/services/services.go +++ b/controllers/platform/services/services.go @@ -214,7 +214,15 @@ func (d DataIndexHandler) ConfigurePersistence(containerSpec *corev1.Container) if d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSQL != nil { p = d.platform.Spec.Services.DataIndex.Persistence.PostgreSQL } else { - p = d.platform.Spec.Persistence.PostgreSQL + p = &operatorapi.PersistencePostgreSQL{ + SecretRef: d.platform.Spec.Persistence.PostgreSQL.SecretRef, + JdbcUrl: d.platform.Spec.Persistence.PostgreSQL.JdbcUrl, + } + if d.platform.Spec.Persistence.PostgreSQL.ServiceRef != nil { + p.ServiceRef = &operatorapi.PostgreSQLServiceOptions{ + SQLServiceOptions: d.platform.Spec.Persistence.PostgreSQL.ServiceRef, + DatabaseSchema: d.GetServiceName()} + } } c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p, d.GetServiceName(), d.platform.Namespace)...) // specific to DataIndex @@ -388,7 +396,15 @@ func (j JobServiceHandler) ConfigurePersistence(containerSpec *corev1.Container) if j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSQL != nil { p = j.platform.Spec.Services.JobService.Persistence.PostgreSQL } else { - p = j.platform.Spec.Persistence.PostgreSQL + p = &operatorapi.PersistencePostgreSQL{ + SecretRef: j.platform.Spec.Persistence.PostgreSQL.SecretRef, + JdbcUrl: j.platform.Spec.Persistence.PostgreSQL.JdbcUrl, + } + if j.platform.Spec.Persistence.PostgreSQL.ServiceRef != nil { + p.ServiceRef = &operatorapi.PostgreSQLServiceOptions{ + SQLServiceOptions: j.platform.Spec.Persistence.PostgreSQL.ServiceRef, + DatabaseSchema: j.GetServiceName()} + } } c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p, j.GetServiceName(), j.platform.Namespace)...) // Specific to Job Service @@ -415,7 +431,15 @@ func (j JobServiceHandler) GenerateServiceProperties() (*properties.Properties, if j.IsServiceSetInSpec() && jspec.Persistence != nil && jspec.Persistence.PostgreSQL != nil { pspec = j.platform.Spec.Services.JobService.Persistence.PostgreSQL } else { - pspec = j.platform.Spec.Persistence.PostgreSQL + pspec = &operatorapi.PersistencePostgreSQL{ + SecretRef: j.platform.Spec.Persistence.PostgreSQL.SecretRef, + JdbcUrl: j.platform.Spec.Persistence.PostgreSQL.JdbcUrl, + } + if j.platform.Spec.Persistence.PostgreSQL.ServiceRef != nil { + pspec.ServiceRef = &operatorapi.PostgreSQLServiceOptions{ + SQLServiceOptions: j.platform.Spec.Persistence.PostgreSQL.ServiceRef, + DatabaseSchema: j.GetServiceName()} + } } dataSourceReactiveURL, err := generateReactiveURL(pspec, j.GetServiceName(), j.platform.Namespace, constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) if err != nil { diff --git a/controllers/profiles/common/object_creators.go b/controllers/profiles/common/object_creators.go index 4806ec497..4641c852d 100644 --- a/controllers/profiles/common/object_creators.go +++ b/controllers/profiles/common/object_creators.go @@ -151,8 +151,19 @@ func defaultContainer(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataF } if workflow.Spec.Persistence != nil { c := workflow.Spec.Persistence - if c.PostgreSQL == nil { - c = plf.Spec.Persistence + if c.PostgreSQL == nil && plf.Spec.Persistence != nil { + c = &operatorapi.PersistenceOptionsSpec{} + if plf.Spec.Persistence.PostgreSQL != nil { + c.PostgreSQL = &operatorapi.PersistencePostgreSQL{ + SecretRef: plf.Spec.Persistence.PostgreSQL.SecretRef, + JdbcUrl: plf.Spec.Persistence.PostgreSQL.JdbcUrl, + } + if plf.Spec.Persistence.PostgreSQL.ServiceRef != nil { + c.PostgreSQL.ServiceRef = &operatorapi.PostgreSQLServiceOptions{ + SQLServiceOptions: plf.Spec.Persistence.PostgreSQL.ServiceRef, + DatabaseSchema: workflow.Name} + } + } } var err error defaultFlowContainer, err = persistence.ConfigurePersistence(defaultFlowContainer, c, workflow.Name, workflow.Namespace) diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index 52b3160be..0031c29da 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -308,10 +308,11 @@ func TestMergePodSpec_OverrideContainers_WithPostgreSQL_In_Workflow_CR(t *testin PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ - Name: "test", - Namespace: "foo", - Port: &postgreSQLPort, - DatabaseName: "petstore", + SQLServiceOptions: &v1alpha08.SQLServiceOptions{ + Name: "test", + Namespace: "foo", + Port: &postgreSQLPort, + DatabaseName: "petstore"}, DatabaseSchema: "bar"}, }, }, @@ -375,19 +376,18 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti Namespace: "default", }, Spec: v1alpha08.SonataFlowPlatformSpec{ - Persistence: &v1alpha08.PersistenceOptionsSpec{ - PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + Persistence: &v1alpha08.PlatformPersistenceOptionsSpec{ + PostgreSQL: &v1alpha08.PlatformPersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", UserKey: "username", PasswordKey: "password", }, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ - Name: "service_name", - Namespace: "service_namespace", - Port: &postgreSQLPort, - DatabaseName: "foo", - DatabaseSchema: "bar", + ServiceRef: &v1alpha08.SQLServiceOptions{ + Name: "service_name", + Namespace: "service_namespace", + Port: &postgreSQLPort, + DatabaseName: "foo", }, }, }, @@ -427,7 +427,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti }, { Name: "QUARKUS_DATASOURCE_JDBC_URL", - Value: "jdbc:postgresql://service_name.service_namespace:5432/foo?currentSchema=bar", + Value: "jdbc:postgresql://service_name.service_namespace:5432/foo?currentSchema=greeting", }, { Name: "KOGITO_PERSISTENCE_TYPE", @@ -450,25 +450,25 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti } func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *testing.T) { + p := &v1alpha08.SonataFlowPlatform{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: "default", }, Spec: v1alpha08.SonataFlowPlatformSpec{ - Persistence: &v1alpha08.PersistenceOptionsSpec{ - PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + Persistence: &v1alpha08.PlatformPersistenceOptionsSpec{ + PostgreSQL: &v1alpha08.PlatformPersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", UserKey: "username", PasswordKey: "password", }, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ - Name: "service_name", - Namespace: "service_namespace", - Port: &postgreSQLPort, - DatabaseName: "foo", - DatabaseSchema: "bar", + ServiceRef: &v1alpha08.SQLServiceOptions{ + Name: "service_name", + Namespace: "service_namespace", + Port: &postgreSQLPort, + DatabaseName: "foo", }, }, }, @@ -497,10 +497,11 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_And_In_Workflow_CR(t *t PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ - Name: "test", - Namespace: "default", - Port: &postgreSQLPort, - DatabaseName: "my_database", + SQLServiceOptions: &v1alpha08.SQLServiceOptions{ + Name: "test", + Namespace: "default", + Port: &postgreSQLPort, + DatabaseName: "my_database"}, DatabaseSchema: "bar"}, }, }, @@ -563,14 +564,14 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Req Namespace: "default", }, Spec: v1alpha08.SonataFlowPlatformSpec{ - Persistence: &v1alpha08.PersistenceOptionsSpec{ - PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + Persistence: &v1alpha08.PlatformPersistenceOptionsSpec{ + PostgreSQL: &v1alpha08.PlatformPersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{ Name: "foo_secret", UserKey: "username", PasswordKey: "password", }, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{ + ServiceRef: &v1alpha08.SQLServiceOptions{ Name: "service_name", Namespace: "service_namespace", Port: &postgreSQLPort, @@ -609,5 +610,5 @@ func TestMergePodSpec_WithEphemeralPostgreSQL_And_Undefined_PostgreSQL_Image_In_ } _, err := DeploymentCreator(workflow, p) assert.Error(t, err) - assert.Equal(t, "no persistence specification found", err.Error()) + assert.Equal(t, "no postgreSQL configuration found", err.Error()) } diff --git a/controllers/profiles/common/persistence/postgresql.go b/controllers/profiles/common/persistence/postgresql.go index fca5ed82e..3543ab921 100644 --- a/controllers/profiles/common/persistence/postgresql.go +++ b/controllers/profiles/common/persistence/postgresql.go @@ -113,9 +113,6 @@ func ConfigurePostgreSQLEnv(postgresql *operatorapi.PersistencePostgreSQL, datab } func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistenceOptionsSpec, defaultSchema, namespace string) (*corev1.Container, error) { - if config == nil { - return nil, fmt.Errorf("no persistence specification found") - } if config.PostgreSQL == nil { return nil, fmt.Errorf("no postgreSQL configuration found") } diff --git a/controllers/sonataflowplatform_controller_test.go b/controllers/sonataflowplatform_controller_test.go index 855041e61..48c7b91c9 100644 --- a/controllers/sonataflowplatform_controller_test.go +++ b/controllers/sonataflowplatform_controller_test.go @@ -138,7 +138,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set ksp.Spec.Services.DataIndex.Persistence = &v1alpha08.PersistenceOptionsSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{SQLServiceOptions: &v1alpha08.SQLServiceOptions{Name: "test"}}, }} // Ensure correct container overriding anything set in PodSpec ksp.Spec.Services.DataIndex.PodTemplate.Container = v1alpha08.ContainerSpec{TerminationMessagePath: "testing"} @@ -262,10 +262,14 @@ func TestSonataFlowPlatformController(t *testing.T) { DataIndex: &v1alpha08.ServiceSpec{}, JobService: &v1alpha08.ServiceSpec{}, }, - Persistence: &v1alpha08.PersistenceOptionsSpec{ - PostgreSQL: &v1alpha08.PersistencePostgreSQL{ - SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, + Persistence: &v1alpha08.PlatformPersistenceOptionsSpec{ + PostgreSQL: &v1alpha08.PlatformPersistencePostgreSQL{ + SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, + ServiceRef: &v1alpha08.SQLServiceOptions{ + Name: "postgresql", + Namespace: "default", + Port: &postgreSQLPort, + DatabaseName: "sonataflow"}, }, }, } @@ -366,10 +370,10 @@ func TestSonataFlowPlatformController(t *testing.T) { }, }, }, - Persistence: &v1alpha08.PersistenceOptionsSpec{ - PostgreSQL: &v1alpha08.PersistencePostgreSQL{ + Persistence: &v1alpha08.PlatformPersistenceOptionsSpec{ + PostgreSQL: &v1alpha08.PlatformPersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "generic", UserKey: "POSTGRESQL_USER", PasswordKey: "POSTGRESQL_PASSWORD"}, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, + ServiceRef: &v1alpha08.SQLServiceOptions{Name: "postgresql", Namespace: "default", Port: &postgreSQLPort, DatabaseName: "sonataflow"}, }, }, } @@ -510,7 +514,7 @@ func TestSonataFlowPlatformController(t *testing.T) { // Check with persistence set ksp.Spec.Services.JobService.Persistence = &v1alpha08.PersistenceOptionsSpec{PostgreSQL: &v1alpha08.PersistencePostgreSQL{ SecretRef: v1alpha08.PostgreSQLSecretOptions{Name: "test"}, - ServiceRef: &v1alpha08.PostgreSQLServiceOptions{Name: "test"}, + ServiceRef: &v1alpha08.PostgreSQLServiceOptions{SQLServiceOptions: &v1alpha08.SQLServiceOptions{Name: "test"}}, }} // Ensure correct container overriding anything set in PodSpec ksp.Spec.Services.JobService.PodTemplate.Container = v1alpha08.ContainerSpec{TerminationMessagePath: "testing"} diff --git a/operator.yaml b/operator.yaml index 9f6302aaa..301dad15c 100644 --- a/operator.yaml +++ b/operator.yaml @@ -938,10 +938,6 @@ spec: 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 diff --git a/test/e2e/workflow_test.go b/test/e2e/workflow_test.go index 6cbeee7eb..cfc24aaf0 100644 --- a/test/e2e/workflow_test.go +++ b/test/e2e/workflow_test.go @@ -208,8 +208,9 @@ var _ = Describe("Validate the persistence ", Ordered, func() { }, 12*time.Minute).Should(BeTrue()) }, Entry("defined in the workflow from an existing kubernetes service as a reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("by_service")), - Entry("defined from the sonataflow platform as reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform")), Entry("defined in the workflow and from the sonataflow platform", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_overwritten_by_service")), + Entry("defined from the sonataflow platform as reference and with DI and JS", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_with_di_and_js_services")), + Entry("defined from the sonataflow platform as reference and without DI and JS", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_without_di_and_js_services")), ) }) diff --git a/test/testdata/platform/services/prod/ephemeral/02-sonataflow_platform.yaml b/test/testdata/platform/services/prod/ephemeral/02-sonataflow_platform.yaml index 573cbf720..52b7d11f7 100644 --- a/test/testdata/platform/services/prod/ephemeral/02-sonataflow_platform.yaml +++ b/test/testdata/platform/services/prod/ephemeral/02-sonataflow_platform.yaml @@ -26,4 +26,3 @@ spec: enabled: true jobService: enabled: true - diff --git a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml index 6aada1b6d..733b0b952 100644 --- a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml @@ -27,7 +27,6 @@ spec: name: db_not_defined port: 3456 databaseName: db_name - databaseSchema: myschema build: config: strategyOptions: diff --git a/test/testdata/workflow/persistence/from_platform/01-postgres.yaml b/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/01-postgres.yaml similarity index 100% rename from test/testdata/workflow/persistence/from_platform/01-postgres.yaml rename to test/testdata/workflow/persistence/from_platform_with_di_and_js_services/01-postgres.yaml diff --git a/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/02-sonataflow_platform.yaml new file mode 100644 index 000000000..2a7db22a6 --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/02-sonataflow_platform.yaml @@ -0,0 +1,51 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform +spec: + persistence: + postgresql: + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: postgres + port: 5432 + databaseName: sonataflow + build: + config: + strategyOptions: + KanikoBuildCacheEnabled: "true" + services: + dataIndex: + enabled: true + podTemplate: + initContainers: + - name: init-postgres + 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;' ] + jobService: + enabled: true + podTemplate: + initContainers: + - name: init-postgres + 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;' ] + diff --git a/test/testdata/workflow/persistence/from_platform/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml similarity index 100% rename from test/testdata/workflow/persistence/from_platform/03-sonataflow_callbackstatetimeouts.sw.yaml rename to test/testdata/workflow/persistence/from_platform_with_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml diff --git a/test/testdata/workflow/persistence/from_platform/kustomization.yaml b/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/kustomization.yaml similarity index 100% rename from test/testdata/workflow/persistence/from_platform/kustomization.yaml rename to test/testdata/workflow/persistence/from_platform_with_di_and_js_services/kustomization.yaml diff --git a/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/01-postgres.yaml b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/01-postgres.yaml new file mode 100644 index 000000000..662de4c7b --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/01-postgres.yaml @@ -0,0 +1,86 @@ +# 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. + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: postgres + template: + metadata: + labels: + app.kubernetes.io/name: postgres + spec: + containers: + - name: postgres + image: postgres:13.2-alpine + imagePullPolicy: 'IfNotPresent' + ports: + - containerPort: 5432 + volumeMounts: + - name: storage + mountPath: /var/lib/postgresql/data + envFrom: + - secretRef: + name: postgres-secrets + readinessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + livenessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + resources: + limits: + memory: "256Mi" + cpu: "500m" + volumes: + - name: storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + selector: + app.kubernetes.io/name: postgres + ports: + - port: 5432 diff --git a/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/02-sonataflow_platform.yaml similarity index 96% rename from test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml rename to test/testdata/workflow/persistence/from_platform_without_di_and_js_services/02-sonataflow_platform.yaml index 9debf41df..b5ac4e161 100644 --- a/test/testdata/workflow/persistence/from_platform/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/02-sonataflow_platform.yaml @@ -27,7 +27,6 @@ spec: name: postgres port: 5432 databaseName: sonataflow - databaseSchema: myschema build: config: strategyOptions: diff --git a/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml new file mode 100644 index 000000000..de52b153d --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml @@ -0,0 +1,86 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlow +metadata: + name: callbackstatetimeouts + annotations: + sonataflow.org/description: Callback State Timeouts Example k8s + sonataflow.org/version: 0.0.1 +spec: + persistence: {} + podTemplate: + container: + env: + - name: QUARKUS_FLYWAY_MIGRATE_AT_START + value: "true" + flow: + start: PrintStartMessage + events: + - name: callbackEvent + source: '' + type: callback_event_type + functions: + - name: systemOut + type: custom + operation: sysout + states: + - name: PrintStartMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has started.\"}" + transition: CallbackState + - name: CallbackState + type: callback + action: + name: callbackAction + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has executed the callbackFunction.\"}" + eventRef: callbackEvent + transition: CheckEventArrival + timeouts: + eventTimeout: PT30S + - name: CheckEventArrival + type: switch + dataConditions: + - condition: "${ .eventData != null }" + transition: EventArrived + defaultCondition: + transition: EventNotArrived + - name: EventArrived + type: inject + data: + exitMessage: "The callback event has arrived." + transition: PrintExitMessage + - name: EventNotArrived + type: inject + data: + exitMessage: "The callback event has not arrived, and the timeout has overdue." + transition: PrintExitMessage + - name: PrintExitMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has finalized. \" + .exitMessage + \" eventData: \" + .eventData}" + end: true diff --git a/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/kustomization.yaml b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/kustomization.yaml new file mode 100644 index 000000000..b7f587bcc --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/kustomization.yaml @@ -0,0 +1,33 @@ +# 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. + +resources: +- 01-postgres.yaml +- 02-sonataflow_platform.yaml +- 03-sonataflow_callbackstatetimeouts.sw.yaml + +generatorOptions: + disableNameSuffixHash: true + +secretGenerator: + - name: postgres-secrets + literals: + - POSTGRES_USER=sonataflow + - POSTGRES_PASSWORD=sonataflow + - POSTGRES_DATABASE=sonataflow + - PGDATA=/var/lib/pgsql/data/userdata + +sortOptions: + order: fifo + From 0a22b9ce8e9c62bb2551446c2acae223c9dc3c14 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Wed, 14 Feb 2024 09:36:38 -0500 Subject: [PATCH 20/22] Changed logic in workflow to use persistence = nil as a signal to the operator to derive the persistence from the platform if available, and persistence = {} to instruct the operator to discard persistence in the workflow, even when the platform has one Signed-off-by: Jordi Gil --- controllers/platform/services/services.go | 36 +------- .../profiles/common/object_creators.go | 27 ++---- .../profiles/common/object_creators_test.go | 21 +---- .../profiles/common/persistence/postgresql.go | 31 ++++++- test/e2e/workflow_test.go | 30 ++++--- ...3-sonataflow_callbackstatetimeouts.sw.yaml | 1 - .../01-postgres.yaml | 86 +++++++++++++++++++ .../02-sonataflow_platform.yaml | 33 +++++++ ...llbackstatetimeouts-no-persistence.sw.yaml | 86 +++++++++++++++++++ .../kustomization.yaml | 33 +++++++ ...3-sonataflow_callbackstatetimeouts.sw.yaml | 1 - 11 files changed, 297 insertions(+), 88 deletions(-) create mode 100644 test/testdata/workflow/persistence/from_platform_with_no_persistence_required/01-postgres.yaml create mode 100644 test/testdata/workflow/persistence/from_platform_with_no_persistence_required/02-sonataflow_platform.yaml create mode 100644 test/testdata/workflow/persistence/from_platform_with_no_persistence_required/03-sonataflow_callbackstatetimeouts-no-persistence.sw.yaml create mode 100644 test/testdata/workflow/persistence/from_platform_with_no_persistence_required/kustomization.yaml diff --git a/controllers/platform/services/services.go b/controllers/platform/services/services.go index 77ef1075d..4f7d82bc9 100644 --- a/controllers/platform/services/services.go +++ b/controllers/platform/services/services.go @@ -206,25 +206,11 @@ func (d DataIndexHandler) hasPostgreSQLConfigured() bool { } func (d DataIndexHandler) ConfigurePersistence(containerSpec *corev1.Container) *corev1.Container { - if d.hasPostgreSQLConfigured() { - var p *operatorapi.PersistencePostgreSQL + p := persistence.RetrieveConfiguration(d.platform.Spec.Services.DataIndex.Persistence, d.platform.Spec.Persistence, d.GetServiceName()) c := containerSpec.DeepCopy() c.Image = d.GetServiceImageName(constants.PersistenceTypePostgreSQL) - if d.platform.Spec.Services.DataIndex.Persistence != nil && d.platform.Spec.Services.DataIndex.Persistence.PostgreSQL != nil { - p = d.platform.Spec.Services.DataIndex.Persistence.PostgreSQL - } else { - p = &operatorapi.PersistencePostgreSQL{ - SecretRef: d.platform.Spec.Persistence.PostgreSQL.SecretRef, - JdbcUrl: d.platform.Spec.Persistence.PostgreSQL.JdbcUrl, - } - if d.platform.Spec.Persistence.PostgreSQL.ServiceRef != nil { - p.ServiceRef = &operatorapi.PostgreSQLServiceOptions{ - SQLServiceOptions: d.platform.Spec.Persistence.PostgreSQL.ServiceRef, - DatabaseSchema: d.GetServiceName()} - } - } - c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p, d.GetServiceName(), d.platform.Namespace)...) + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p.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 @@ -426,22 +412,8 @@ func (j JobServiceHandler) GenerateServiceProperties() (*properties.Properties, props.Set(constants.JobServiceKafkaSmallRyeHealthProperty, "false") // add data source reactive URL if j.hasPostgreSQLConfigured() { - jspec := j.platform.Spec.Services.JobService - var pspec *operatorapi.PersistencePostgreSQL - if j.IsServiceSetInSpec() && jspec.Persistence != nil && jspec.Persistence.PostgreSQL != nil { - pspec = j.platform.Spec.Services.JobService.Persistence.PostgreSQL - } else { - pspec = &operatorapi.PersistencePostgreSQL{ - SecretRef: j.platform.Spec.Persistence.PostgreSQL.SecretRef, - JdbcUrl: j.platform.Spec.Persistence.PostgreSQL.JdbcUrl, - } - if j.platform.Spec.Persistence.PostgreSQL.ServiceRef != nil { - pspec.ServiceRef = &operatorapi.PostgreSQLServiceOptions{ - SQLServiceOptions: j.platform.Spec.Persistence.PostgreSQL.ServiceRef, - DatabaseSchema: j.GetServiceName()} - } - } - dataSourceReactiveURL, err := generateReactiveURL(pspec, j.GetServiceName(), j.platform.Namespace, constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) + p := persistence.RetrieveConfiguration(j.platform.Spec.Services.JobService.Persistence, j.platform.Spec.Persistence, j.GetServiceName()) + dataSourceReactiveURL, err := generateReactiveURL(p.PostgreSQL, j.GetServiceName(), j.platform.Namespace, constants.DefaultDatabaseName, constants.DefaultPostgreSQLPort) if err != nil { return nil, err } diff --git a/controllers/profiles/common/object_creators.go b/controllers/profiles/common/object_creators.go index 4641c852d..7e08366b1 100644 --- a/controllers/profiles/common/object_creators.go +++ b/controllers/profiles/common/object_creators.go @@ -149,27 +149,12 @@ func defaultContainer(workflow *operatorapi.SonataFlow, plf *operatorapi.SonataF if err := mergo.Merge(defaultFlowContainer, workflow.Spec.PodTemplate.Container.ToContainer(), mergo.WithOverride); err != nil { return nil, err } - if workflow.Spec.Persistence != nil { - c := workflow.Spec.Persistence - if c.PostgreSQL == nil && plf.Spec.Persistence != nil { - c = &operatorapi.PersistenceOptionsSpec{} - if plf.Spec.Persistence.PostgreSQL != nil { - c.PostgreSQL = &operatorapi.PersistencePostgreSQL{ - SecretRef: plf.Spec.Persistence.PostgreSQL.SecretRef, - JdbcUrl: plf.Spec.Persistence.PostgreSQL.JdbcUrl, - } - if plf.Spec.Persistence.PostgreSQL.ServiceRef != nil { - c.PostgreSQL.ServiceRef = &operatorapi.PostgreSQLServiceOptions{ - SQLServiceOptions: plf.Spec.Persistence.PostgreSQL.ServiceRef, - DatabaseSchema: workflow.Name} - } - } - } - var err error - defaultFlowContainer, err = persistence.ConfigurePersistence(defaultFlowContainer, c, workflow.Name, workflow.Namespace) - if err != nil { - return nil, err - } + var pper *operatorapi.PlatformPersistenceOptionsSpec + if plf != nil && plf.Spec.Persistence != nil { + pper = plf.Spec.Persistence + } + if p := persistence.RetrieveConfiguration(workflow.Spec.Persistence, pper, workflow.Name); p != nil { + defaultFlowContainer = persistence.ConfigurePersistence(defaultFlowContainer, p, workflow.Name, workflow.Namespace) } // immutable defaultFlowContainer.Name = operatorapi.DefaultContainerName diff --git a/controllers/profiles/common/object_creators_test.go b/controllers/profiles/common/object_creators_test.go index 0031c29da..beb04369e 100644 --- a/controllers/profiles/common/object_creators_test.go +++ b/controllers/profiles/common/object_creators_test.go @@ -396,7 +396,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_CR_And_Worflow_Requesti workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ - Persistence: &v1alpha08.PersistenceOptionsSpec{}, + Persistence: nil, } object, err := DeploymentCreator(workflow, p) assert.NoError(t, err) @@ -583,7 +583,7 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Req } workflow := test.GetBaseSonataFlow(t.Name()) workflow.Spec = v1alpha08.SonataFlowSpec{ - Persistence: nil, + Persistence: &v1alpha08.PersistenceOptionsSpec{}, } object, err := DeploymentCreator(workflow, p) assert.NoError(t, err) @@ -595,20 +595,3 @@ func TestMergePodSpec_WithServicedPostgreSQL_In_Platform_But_Workflow_CR_Not_Req assert.Equal(t, int32(8080), flowContainer.Ports[0].ContainerPort) assert.Nil(t, flowContainer.Env) } - -func TestMergePodSpec_WithEphemeralPostgreSQL_And_Undefined_PostgreSQL_Image_In_Platform_Spec(t *testing.T) { - p := &v1alpha08.SonataFlowPlatform{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: "default", - }, - Spec: v1alpha08.SonataFlowPlatformSpec{}, - } - workflow := test.GetBaseSonataFlow(t.Name()) - workflow.Spec = v1alpha08.SonataFlowSpec{ - Persistence: &v1alpha08.PersistenceOptionsSpec{}, - } - _, err := DeploymentCreator(workflow, p) - assert.Error(t, err) - assert.Equal(t, "no postgreSQL configuration found", err.Error()) -} diff --git a/controllers/profiles/common/persistence/postgresql.go b/controllers/profiles/common/persistence/postgresql.go index 3543ab921..6da517bf0 100644 --- a/controllers/profiles/common/persistence/postgresql.go +++ b/controllers/profiles/common/persistence/postgresql.go @@ -19,6 +19,7 @@ import ( corev1 "k8s.io/api/core/v1" + "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08" operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08" "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants" ) @@ -112,11 +113,35 @@ func ConfigurePostgreSQLEnv(postgresql *operatorapi.PersistencePostgreSQL, datab } } -func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistenceOptionsSpec, defaultSchema, namespace string) (*corev1.Container, error) { +func ConfigurePersistence(serviceContainer *corev1.Container, config *operatorapi.PersistenceOptionsSpec, defaultSchema, namespace string) *corev1.Container { if config.PostgreSQL == nil { - return nil, fmt.Errorf("no postgreSQL configuration found") + return serviceContainer } c := serviceContainer.DeepCopy() c.Env = append(c.Env, ConfigurePostgreSQLEnv(config.PostgreSQL, defaultSchema, namespace)...) - return c, nil + return c +} + +func RetrieveConfiguration(primary *v1alpha08.PersistenceOptionsSpec, platformPersistence *v1alpha08.PlatformPersistenceOptionsSpec, schema string) *v1alpha08.PersistenceOptionsSpec { + if primary != nil { + return primary + } + if platformPersistence == nil { + return nil + } + c := &v1alpha08.PersistenceOptionsSpec{} + if platformPersistence.PostgreSQL != nil { + c.PostgreSQL = &v1alpha08.PersistencePostgreSQL{ + SecretRef: platformPersistence.PostgreSQL.SecretRef, + } + if platformPersistence.PostgreSQL.ServiceRef != nil { + c.PostgreSQL.ServiceRef = &v1alpha08.PostgreSQLServiceOptions{ + SQLServiceOptions: platformPersistence.PostgreSQL.ServiceRef, + DatabaseSchema: schema, + } + } else { + c.PostgreSQL.JdbcUrl = platformPersistence.PostgreSQL.JdbcUrl + } + } + return c } diff --git a/test/e2e/workflow_test.go b/test/e2e/workflow_test.go index cfc24aaf0..8815feadb 100644 --- a/test/e2e/workflow_test.go +++ b/test/e2e/workflow_test.go @@ -164,7 +164,7 @@ var _ = Describe("Validate the persistence ", Ordered, func() { }) - DescribeTable("when deploying a SonataFlow CR with PostgreSQL persistence", func(testcaseDir string) { + DescribeTable("when deploying a SonataFlow CR with PostgreSQL persistence", func(testcaseDir string, withPersistence bool) { By("Deploy the CR") var manifests []byte EventuallyWithOffset(1, func() error { @@ -180,14 +180,14 @@ var _ = Describe("Validate the persistence ", Ordered, func() { By("Wait for SonatatFlow CR to complete deployment") // wait for service deployments to be ready EventuallyWithOffset(1, func() error { - cmd = exec.Command("kubectl", "wait", "pod", "-n", ns, "-l", "sonataflow.org/workflow-app=callbackstatetimeouts", "--for", "condition=Ready", "--timeout=5s") + cmd = exec.Command("kubectl", "wait", "pod", "-n", ns, "-l", "sonataflow.org/workflow-app", "--for", "condition=Ready", "--timeout=5s") out, err := utils.Run(cmd) GinkgoWriter.Printf("%s\n", string(out)) return err }, 12*time.Minute, 5).Should(Succeed()) By("Evaluate status of the workflow's pod database connection health endpoint") - cmd = exec.Command("kubectl", "get", "pod", "-l", "sonataflow.org/workflow-app=callbackstatetimeouts", "-n", ns, "-ojsonpath={.items[*].metadata.name}") + cmd = exec.Command("kubectl", "get", "pod", "-l", "sonataflow.org/workflow-app", "-n", ns, "-ojsonpath={.items[*].metadata.name}") output, err := utils.Run(cmd) Expect(err).NotTo(HaveOccurred()) EventuallyWithOffset(1, func() bool { @@ -198,19 +198,27 @@ var _ = Describe("Validate the persistence ", Ordered, func() { } Expect(h.Status).To(Equal(upStatus), "Pod health is not UP") for _, c := range h.Checks { - if c.Name == "Database connections health check" { - Expect(c.Status).To(Equal(upStatus), "Pod's database connection is not UP") - return true + if withPersistence { + if c.Name == "Database connections health check" { + Expect(c.Status).To(Equal(upStatus), "Pod's database connection is not UP") + return true + } + } else { + Expect(c.Name).NotTo(Equal("Database connections health check"), "persistence configuration found in workflow") } } + if !withPersistence { + return true + } } return false - }, 12*time.Minute).Should(BeTrue()) + }, 1).Should(BeTrue()) }, - Entry("defined in the workflow from an existing kubernetes service as a reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("by_service")), - Entry("defined in the workflow and from the sonataflow platform", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_overwritten_by_service")), - Entry("defined from the sonataflow platform as reference and with DI and JS", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_with_di_and_js_services")), - Entry("defined from the sonataflow platform as reference and without DI and JS", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_without_di_and_js_services")), + Entry("defined in the workflow from an existing kubernetes service as a reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("by_service"), true), + Entry("defined in the workflow and from the sonataflow platform", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_overwritten_by_service"), true), + Entry("defined from the sonataflow platform as reference and with DI and JS", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_with_di_and_js_services"), true), + Entry("defined from the sonataflow platform as reference and without DI and JS", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_without_di_and_js_services"), true), + Entry("defined from the sonataflow platform as reference but not required by the workflow", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_with_no_persistence_required"), false), ) }) diff --git a/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml index de52b153d..1d4bdbb96 100644 --- a/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml +++ b/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml @@ -20,7 +20,6 @@ metadata: sonataflow.org/description: Callback State Timeouts Example k8s sonataflow.org/version: 0.0.1 spec: - persistence: {} podTemplate: container: env: diff --git a/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/01-postgres.yaml b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/01-postgres.yaml new file mode 100644 index 000000000..662de4c7b --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/01-postgres.yaml @@ -0,0 +1,86 @@ +# 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. + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: postgres + template: + metadata: + labels: + app.kubernetes.io/name: postgres + spec: + containers: + - name: postgres + image: postgres:13.2-alpine + imagePullPolicy: 'IfNotPresent' + ports: + - containerPort: 5432 + volumeMounts: + - name: storage + mountPath: /var/lib/postgresql/data + envFrom: + - secretRef: + name: postgres-secrets + readinessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + livenessProbe: + exec: + command: ["pg_isready"] + initialDelaySeconds: 15 + timeoutSeconds: 2 + resources: + limits: + memory: "256Mi" + cpu: "500m" + volumes: + - name: storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: postgres + name: postgres +spec: + selector: + app.kubernetes.io/name: postgres + ports: + - port: 5432 diff --git a/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/02-sonataflow_platform.yaml new file mode 100644 index 000000000..817ce0c31 --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/02-sonataflow_platform.yaml @@ -0,0 +1,33 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform +spec: + persistence: + postgresql: + secretRef: + name: postgres-secrets + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: postgres + port: 5432 + databaseName: sonataflow + build: + config: + strategyOptions: + KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/03-sonataflow_callbackstatetimeouts-no-persistence.sw.yaml b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/03-sonataflow_callbackstatetimeouts-no-persistence.sw.yaml new file mode 100644 index 000000000..7a8226c62 --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/03-sonataflow_callbackstatetimeouts-no-persistence.sw.yaml @@ -0,0 +1,86 @@ +# 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlow +metadata: + name: callbackstatetimeouts-no-persistence + annotations: + sonataflow.org/description: Callback State Timeouts Example k8s + sonataflow.org/version: 0.0.1 +spec: + persistence: {} + podTemplate: + container: + env: + - name: QUARKUS_FLYWAY_MIGRATE_AT_START + value: "true" + flow: + start: PrintStartMessage + events: + - name: callbackEvent + source: '' + type: callback_event_type + functions: + - name: systemOut + type: custom + operation: sysout + states: + - name: PrintStartMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has started.\"}" + transition: CallbackState + - name: CallbackState + type: callback + action: + name: callbackAction + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has executed the callbackFunction.\"}" + eventRef: callbackEvent + transition: CheckEventArrival + timeouts: + eventTimeout: PT30S + - name: CheckEventArrival + type: switch + dataConditions: + - condition: "${ .eventData != null }" + transition: EventArrived + defaultCondition: + transition: EventNotArrived + - name: EventArrived + type: inject + data: + exitMessage: "The callback event has arrived." + transition: PrintExitMessage + - name: EventNotArrived + type: inject + data: + exitMessage: "The callback event has not arrived, and the timeout has overdue." + transition: PrintExitMessage + - name: PrintExitMessage + type: operation + actions: + - name: printSystemOut + functionRef: + refName: systemOut + arguments: + message: "${\"callback-state-timeouts: \" + $WORKFLOW.instanceId + \" has finalized. \" + .exitMessage + \" eventData: \" + .eventData}" + end: true diff --git a/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/kustomization.yaml b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/kustomization.yaml new file mode 100644 index 000000000..0a410cde1 --- /dev/null +++ b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/kustomization.yaml @@ -0,0 +1,33 @@ +# 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. + +resources: +- 01-postgres.yaml +- 02-sonataflow_platform.yaml +- 03-sonataflow_callbackstatetimeouts-no-persistence.sw.yaml + +generatorOptions: + disableNameSuffixHash: true + +secretGenerator: + - name: postgres-secrets + literals: + - POSTGRES_USER=sonataflow + - POSTGRES_PASSWORD=sonataflow + - POSTGRES_DATABASE=sonataflow + - PGDATA=/var/lib/pgsql/data/userdata + +sortOptions: + order: fifo + diff --git a/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml index de52b153d..1d4bdbb96 100644 --- a/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml +++ b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/03-sonataflow_callbackstatetimeouts.sw.yaml @@ -20,7 +20,6 @@ metadata: sonataflow.org/description: Callback State Timeouts Example k8s sonataflow.org/version: 0.0.1 spec: - persistence: {} podTemplate: container: env: From 214e0b2d9ef5a8bbbf6c083677e62470648a9106 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Thu, 15 Feb 2024 15:42:19 -0500 Subject: [PATCH 21/22] Refactored ConfigurePersistence() in JobServiceHandler to use function RetrievePersistence() --- controllers/platform/services/services.go | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/controllers/platform/services/services.go b/controllers/platform/services/services.go index 4f7d82bc9..33a275fd2 100644 --- a/controllers/platform/services/services.go +++ b/controllers/platform/services/services.go @@ -376,23 +376,10 @@ func (j JobServiceHandler) hasPostgreSQLConfigured() bool { func (j JobServiceHandler) ConfigurePersistence(containerSpec *corev1.Container) *corev1.Container { if j.hasPostgreSQLConfigured() { - var p *operatorapi.PersistencePostgreSQL c := containerSpec.DeepCopy() c.Image = j.GetServiceImageName(constants.PersistenceTypePostgreSQL) - if j.platform.Spec.Services.JobService.Persistence != nil && j.platform.Spec.Services.JobService.Persistence.PostgreSQL != nil { - p = j.platform.Spec.Services.JobService.Persistence.PostgreSQL - } else { - p = &operatorapi.PersistencePostgreSQL{ - SecretRef: j.platform.Spec.Persistence.PostgreSQL.SecretRef, - JdbcUrl: j.platform.Spec.Persistence.PostgreSQL.JdbcUrl, - } - if j.platform.Spec.Persistence.PostgreSQL.ServiceRef != nil { - p.ServiceRef = &operatorapi.PostgreSQLServiceOptions{ - SQLServiceOptions: j.platform.Spec.Persistence.PostgreSQL.ServiceRef, - DatabaseSchema: j.GetServiceName()} - } - } - c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p, j.GetServiceName(), j.platform.Namespace)...) + p := persistence.RetrieveConfiguration(j.platform.Spec.Services.JobService.Persistence, j.platform.Spec.Persistence, j.GetServiceName()) + c.Env = append(c.Env, persistence.ConfigurePostgreSQLEnv(p.PostgreSQL, j.GetServiceName(), j.platform.Namespace)...) // Specific to Job Service c.Env = append(c.Env, corev1.EnvVar{Name: "QUARKUS_FLYWAY_MIGRATE_AT_START", Value: "true"}) return c From 9865cca3be339a5ee019a71c58b0a1160d28e708 Mon Sep 17 00:00:00 2001 From: Jordi Gil Date: Tue, 27 Feb 2024 17:40:16 -0500 Subject: [PATCH 22/22] Fix e2e tests and add 'time.Minute' timeout to waitFor condition Signed-off-by: Jordi Gil --- test/e2e/helpers.go | 5 +++-- test/e2e/workflow_test.go | 21 +++++++++++-------- .../02-sonataflow_platform.yaml | 4 ++++ .../02-sonataflow_platform.yaml | 4 ++++ .../02-sonataflow_platform.yaml | 4 ++++ .../02-sonataflow_platform.yaml | 4 ++++ 6 files changed, 31 insertions(+), 11 deletions(-) diff --git a/test/e2e/helpers.go b/test/e2e/helpers.go index 345dc2a69..d2dd8e83b 100644 --- a/test/e2e/helpers.go +++ b/test/e2e/helpers.go @@ -44,8 +44,9 @@ type health struct { } type check struct { - Name string `json:"name"` - Status string `json:"status"` + Name string `json:"name"` + Status string `json:"status"` + Data map[string]string `json:"data"` } var ( diff --git a/test/e2e/workflow_test.go b/test/e2e/workflow_test.go index 8815feadb..30a0ae2fd 100644 --- a/test/e2e/workflow_test.go +++ b/test/e2e/workflow_test.go @@ -144,6 +144,10 @@ var _ = Describe("SonataFlow Operator", Ordered, func() { var _ = Describe("Validate the persistence ", Ordered, func() { + const ( + dbConnectionName = "Database connections health check" + defaultDataCheck = "" + ) var ( ns string ) @@ -198,21 +202,20 @@ var _ = Describe("Validate the persistence ", Ordered, func() { } Expect(h.Status).To(Equal(upStatus), "Pod health is not UP") for _, c := range h.Checks { - if withPersistence { - if c.Name == "Database connections health check" { - Expect(c.Status).To(Equal(upStatus), "Pod's database connection is not UP") + if c.Name == dbConnectionName { + Expect(c.Status).To(Equal(upStatus), "Pod's database connection is not UP") + if withPersistence { + Expect(c.Data[defaultDataCheck]).To(Equal(upStatus), "Pod's 'default' database data is not UP") + return true + } else { + Expect(defaultDataCheck).NotTo(BeElementOf(c.Data), "Pod's 'default' database data check exists in health manifest") return true } - } else { - Expect(c.Name).NotTo(Equal("Database connections health check"), "persistence configuration found in workflow") } } - if !withPersistence { - return true - } } return false - }, 1).Should(BeTrue()) + }, 1*time.Minute).Should(BeTrue()) }, Entry("defined in the workflow from an existing kubernetes service as a reference", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("by_service"), true), Entry("defined in the workflow and from the sonataflow platform", test.GetSonataFlowE2EWorkflowPersistenceSampleDataDirectory("from_platform_overwritten_by_service"), true), diff --git a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml index 733b0b952..e342b1c18 100644 --- a/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform_overwritten_by_service/02-sonataflow_platform.yaml @@ -28,6 +28,10 @@ spec: port: 3456 databaseName: db_name build: + template: + buildArgs: + - name: QUARKUS_EXTENSIONS + value: org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final config: strategyOptions: KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/02-sonataflow_platform.yaml index 2a7db22a6..66eec55a4 100644 --- a/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform_with_di_and_js_services/02-sonataflow_platform.yaml @@ -28,6 +28,10 @@ spec: port: 5432 databaseName: sonataflow build: + template: + buildArgs: + - name: QUARKUS_EXTENSIONS + value: org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final config: strategyOptions: KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/02-sonataflow_platform.yaml index 817ce0c31..f61f4a849 100644 --- a/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform_with_no_persistence_required/02-sonataflow_platform.yaml @@ -28,6 +28,10 @@ spec: port: 5432 databaseName: sonataflow build: + template: + buildArgs: + - name: QUARKUS_EXTENSIONS + value: org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final config: strategyOptions: KanikoBuildCacheEnabled: "true" diff --git a/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/02-sonataflow_platform.yaml b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/02-sonataflow_platform.yaml index b5ac4e161..6dc311d62 100644 --- a/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/02-sonataflow_platform.yaml +++ b/test/testdata/workflow/persistence/from_platform_without_di_and_js_services/02-sonataflow_platform.yaml @@ -28,6 +28,10 @@ spec: port: 5432 databaseName: sonataflow build: + template: + buildArgs: + - name: QUARKUS_EXTENSIONS + value: org.kie.kogito:kogito-addons-quarkus-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final config: strategyOptions: KanikoBuildCacheEnabled: "true"