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

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


The following commit(s) were added to refs/heads/main by this push:
     new 7c848392 Closes #436 - Introduce SonataFlowPlatform flow properties 
propagation (#443)
7c848392 is described below

commit 7c848392e21f0822a1bafabfe1b3104f843df8e1
Author: Ricardo Zanini <[email protected]>
AuthorDate: Tue Apr 16 12:22:40 2024 -0300

    Closes #436 - Introduce SonataFlowPlatform flow properties propagation 
(#443)
---
 Makefile                                           |   2 +-
 api/v1alpha08/sonataflowplatform_property_types.go |  53 +++++++++
 api/v1alpha08/sonataflowplatform_types.go          |   9 +-
 api/v1alpha08/zz_generated.deepcopy.go             |  72 +++++++++++++
 .../sonataflow.org_sonataflowplatforms.yaml        |  74 ++++++++++++-
 .../bases/sonataflow.org_sonataflowplatforms.yaml  |  74 ++++++++++++-
 controllers/cfg/controllers_cfg.go                 |   3 +
 controllers/platform/services/properties.go        |   8 +-
 controllers/profiles/common/ensurer.go             |   2 +-
 controllers/profiles/common/mutate_visitors.go     |   5 +-
 controllers/profiles/common/object_creators.go     |   3 +-
 .../profiles/common/object_creators_test.go        |   1 -
 .../properties/{properties.go => knative.go}       |   2 +-
 .../properties/{application.go => managed.go}      |  73 +++++++------
 .../{application_test.go => managed_test.go}       |  30 +++---
 controllers/profiles/common/properties/platform.go |  87 +++++++++++++++
 .../profiles/common/properties/platform_test.go    | 119 +++++++++++++++++++++
 .../profiles/common/properties/properties_test.go  |  28 +++++
 hack/local/run-operator.sh                         |   2 +
 main.go                                            |   6 +-
 operator.yaml                                      |  74 ++++++++++++-
 ...onataflow.org_v1alpha08_sonataflowplatform.yaml |   4 +
 .../by_service/02-sonataflow_platform.yaml         |   2 +-
 .../02-sonataflow_platform.yaml                    |   2 +-
 .../02-sonataflow_platform.yaml                    |   2 +-
 .../02-sonataflow_platform.yaml                    |   2 +-
 .../02-sonataflow_platform.yaml                    |   2 +-
 utils/client.go                                    |  32 ++++++
 utils/properties.go                                |  18 ++--
 29 files changed, 708 insertions(+), 83 deletions(-)

diff --git a/Makefile b/Makefile
index e704728c..ce87602d 100644
--- a/Makefile
+++ b/Makefile
@@ -158,7 +158,7 @@ run: manifests generate ## Run a controller from your host.
 
 .PHONY: debug
 debug: build-4-debug ## Run a controller from your host from binary
-       ./bin/manager -v=2
+       ./bin/manager -v=2 
-controller-cfg-path=$(CURDIR)/config/manager/controllers_cfg.yaml
 
 # This is currently done directly into the CI
 # PLATFORMS defines the target platforms for the manager image be build to 
provide support to multiple
diff --git a/api/v1alpha08/sonataflowplatform_property_types.go 
b/api/v1alpha08/sonataflowplatform_property_types.go
new file mode 100644
index 00000000..d138ac40
--- /dev/null
+++ b/api/v1alpha08/sonataflowplatform_property_types.go
@@ -0,0 +1,53 @@
+// Copyright 2024 Apache Software Foundation (ASF)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package v1alpha08
+
+import v1 "k8s.io/api/core/v1"
+
+// PropertyPlatformSpec defines the struct for global managed properties in 
the SonataFlowPlatform.
+// These properties are ignored in the SonataFlowClusterPlatform since a 
source of a property (PropertyVarSource) can only be local.
+type PropertyPlatformSpec struct {
+       // Properties that will be added to the SonataFlow managed configMaps 
in the current context.
+       // +optional
+       // +patchMergeKey=name
+       // +patchStrategy=merge
+       Flow []PropertyVar `json:"flow,omitempty" patchStrategy:"merge" 
patchMergeKey:"name"`
+}
+
+// PropertyVar is the entry for a property set derived from the Kubernetes API 
EnvVar.
+// Note that the name doesn't have to match C_IDENTIFIER.
+type PropertyVar struct {
+       // The property name
+       Name string `json:"name"`
+
+       // Optional: no more than one of the following may be specified.
+
+       // Defaults to "".
+       // +optional
+       Value string `json:"value,omitempty"`
+       // Source for the property's value. Cannot be used if value is not 
empty.
+       // +optional
+       ValueFrom *PropertyVarSource `json:"valueFrom,omitempty"`
+}
+
+// PropertyVarSource is the definition of a property source derived from the 
Kubernetes API EnvVarSource.
+type PropertyVarSource struct {
+       // Selects a key of a ConfigMap.
+       // +optional
+       ConfigMapKeyRef *v1.ConfigMapKeySelector 
`json:"configMapKeyRef,omitempty"`
+       // Selects a key of a secret in the flow's namespace
+       // +optional
+       SecretKeyRef *v1.SecretKeySelector `json:"secretKeyRef,omitempty"`
+}
diff --git a/api/v1alpha08/sonataflowplatform_types.go 
b/api/v1alpha08/sonataflowplatform_types.go
index 9dfff7ce..26276fb8 100644
--- a/api/v1alpha08/sonataflowplatform_types.go
+++ b/api/v1alpha08/sonataflowplatform_types.go
@@ -48,10 +48,17 @@ type SonataFlowPlatformSpec struct {
        // 
+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
+       // the configuration is used as the persistence for platform services 
and SonataFlow instances
        // that don't provide one of their own.
        // +optional
        Persistence *PlatformPersistenceOptionsSpec 
`json:"persistence,omitempty"`
+       // Properties defines the property set for a given actor in the current 
context.
+       // For example, the workflow managed properties. One can define here a 
set of properties for SonataFlow deployments
+       // that will be reused across every workflow deployment.
+       //
+       // These properties MAY NOT be propagated to a 
SonataFlowClusterPlatform since PropertyVarSource can only refer local context 
sources.
+       // +optional
+       Properties *PropertyPlatformSpec `json:"properties,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 b311b3a8..c970b108 100644
--- a/api/v1alpha08/zz_generated.deepcopy.go
+++ b/api/v1alpha08/zz_generated.deepcopy.go
@@ -662,6 +662,73 @@ func (in *PostgreSQLServiceOptions) DeepCopy() 
*PostgreSQLServiceOptions {
        return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *PropertyPlatformSpec) DeepCopyInto(out *PropertyPlatformSpec) {
+       *out = *in
+       if in.Flow != nil {
+               in, out := &in.Flow, &out.Flow
+               *out = make([]PropertyVar, len(*in))
+               for i := range *in {
+                       (*in)[i].DeepCopyInto(&(*out)[i])
+               }
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new PropertyPlatformSpec.
+func (in *PropertyPlatformSpec) DeepCopy() *PropertyPlatformSpec {
+       if in == nil {
+               return nil
+       }
+       out := new(PropertyPlatformSpec)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *PropertyVar) DeepCopyInto(out *PropertyVar) {
+       *out = *in
+       if in.ValueFrom != nil {
+               in, out := &in.ValueFrom, &out.ValueFrom
+               *out = new(PropertyVarSource)
+               (*in).DeepCopyInto(*out)
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new PropertyVar.
+func (in *PropertyVar) DeepCopy() *PropertyVar {
+       if in == nil {
+               return nil
+       }
+       out := new(PropertyVar)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *PropertyVarSource) DeepCopyInto(out *PropertyVarSource) {
+       *out = *in
+       if in.ConfigMapKeyRef != nil {
+               in, out := &in.ConfigMapKeyRef, &out.ConfigMapKeyRef
+               *out = new(v1.ConfigMapKeySelector)
+               (*in).DeepCopyInto(*out)
+       }
+       if in.SecretKeyRef != nil {
+               in, out := &in.SecretKeyRef, &out.SecretKeyRef
+               *out = new(v1.SecretKeySelector)
+               (*in).DeepCopyInto(*out)
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new PropertyVarSource.
+func (in *PropertyVarSource) DeepCopy() *PropertyVarSource {
+       if in == nil {
+               return nil
+       }
+       out := new(PropertyVarSource)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *RegistrySpec) DeepCopyInto(out *RegistrySpec) {
        *out = *in
@@ -1124,6 +1191,11 @@ func (in *SonataFlowPlatformSpec) DeepCopyInto(out 
*SonataFlowPlatformSpec) {
                *out = new(PlatformPersistenceOptionsSpec)
                (*in).DeepCopyInto(*out)
        }
+       if in.Properties != nil {
+               in, out := &in.Properties, &out.Properties
+               *out = new(PropertyPlatformSpec)
+               (*in).DeepCopyInto(*out)
+       }
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SonataFlowPlatformSpec.
diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml 
b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml
index 29791ec8..09a2f254 100644
--- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml
+++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml
@@ -423,7 +423,7 @@ spec:
               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
+                  for platform services and SonataFlow instances that don't 
provide
                   one of their own.
                 maxProperties: 1
                 properties:
@@ -477,6 +477,78 @@ spec:
                     - secretRef
                     type: object
                 type: object
+              properties:
+                description: "Properties defines the property set for a given 
actor
+                  in the current context. For example, the workflow managed 
properties.
+                  One can define here a set of properties for SonataFlow 
deployments
+                  that will be reused across every workflow deployment. \n 
These properties
+                  MAY NOT be propagated to a SonataFlowClusterPlatform since 
PropertyVarSource
+                  can only refer local context sources."
+                properties:
+                  flow:
+                    description: Properties that will be added to the 
SonataFlow managed
+                      configMaps in the current context.
+                    items:
+                      description: PropertyVar is the entry for a property set 
derived
+                        from the Kubernetes API EnvVar. Note that the name 
doesn't
+                        have to match C_IDENTIFIER.
+                      properties:
+                        name:
+                          description: The property name
+                          type: string
+                        value:
+                          description: Defaults to "".
+                          type: string
+                        valueFrom:
+                          description: Source for the property's value. Cannot 
be
+                            used if value is not empty.
+                          properties:
+                            configMapKeyRef:
+                              description: Selects a key of a ConfigMap.
+                              properties:
+                                key:
+                                  description: The key to select.
+                                  type: string
+                                name:
+                                  description: 'Name of the referent. More 
info: 
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                    TODO: Add other useful fields. apiVersion, 
kind,
+                                    uid?'
+                                  type: string
+                                optional:
+                                  description: Specify whether the ConfigMap 
or its
+                                    key must be defined
+                                  type: boolean
+                              required:
+                              - key
+                              type: object
+                              x-kubernetes-map-type: atomic
+                            secretKeyRef:
+                              description: Selects a key of a secret in the 
flow's
+                                namespace
+                              properties:
+                                key:
+                                  description: The key of the secret to select 
from.  Must
+                                    be a valid secret key.
+                                  type: string
+                                name:
+                                  description: 'Name of the referent. More 
info: 
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                    TODO: Add other useful fields. apiVersion, 
kind,
+                                    uid?'
+                                  type: string
+                                optional:
+                                  description: Specify whether the Secret or 
its key
+                                    must be defined
+                                  type: boolean
+                              required:
+                              - key
+                              type: object
+                              x-kubernetes-map-type: atomic
+                          type: object
+                      required:
+                      - name
+                      type: object
+                    type: array
+                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_sonataflowplatforms.yaml 
b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml
index 8d433833..648b1bcd 100644
--- a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml
+++ b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml
@@ -424,7 +424,7 @@ spec:
               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
+                  for platform services and SonataFlow instances that don't 
provide
                   one of their own.
                 maxProperties: 1
                 properties:
@@ -478,6 +478,78 @@ spec:
                     - secretRef
                     type: object
                 type: object
+              properties:
+                description: "Properties defines the property set for a given 
actor
+                  in the current context. For example, the workflow managed 
properties.
+                  One can define here a set of properties for SonataFlow 
deployments
+                  that will be reused across every workflow deployment. \n 
These properties
+                  MAY NOT be propagated to a SonataFlowClusterPlatform since 
PropertyVarSource
+                  can only refer local context sources."
+                properties:
+                  flow:
+                    description: Properties that will be added to the 
SonataFlow managed
+                      configMaps in the current context.
+                    items:
+                      description: PropertyVar is the entry for a property set 
derived
+                        from the Kubernetes API EnvVar. Note that the name 
doesn't
+                        have to match C_IDENTIFIER.
+                      properties:
+                        name:
+                          description: The property name
+                          type: string
+                        value:
+                          description: Defaults to "".
+                          type: string
+                        valueFrom:
+                          description: Source for the property's value. Cannot 
be
+                            used if value is not empty.
+                          properties:
+                            configMapKeyRef:
+                              description: Selects a key of a ConfigMap.
+                              properties:
+                                key:
+                                  description: The key to select.
+                                  type: string
+                                name:
+                                  description: 'Name of the referent. More 
info: 
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                    TODO: Add other useful fields. apiVersion, 
kind,
+                                    uid?'
+                                  type: string
+                                optional:
+                                  description: Specify whether the ConfigMap 
or its
+                                    key must be defined
+                                  type: boolean
+                              required:
+                              - key
+                              type: object
+                              x-kubernetes-map-type: atomic
+                            secretKeyRef:
+                              description: Selects a key of a secret in the 
flow's
+                                namespace
+                              properties:
+                                key:
+                                  description: The key of the secret to select 
from.  Must
+                                    be a valid secret key.
+                                  type: string
+                                name:
+                                  description: 'Name of the referent. More 
info: 
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                    TODO: Add other useful fields. apiVersion, 
kind,
+                                    uid?'
+                                  type: string
+                                optional:
+                                  description: Specify whether the Secret or 
its key
+                                    must be defined
+                                  type: boolean
+                              required:
+                              - key
+                              type: object
+                              x-kubernetes-map-type: atomic
+                          type: object
+                      required:
+                      - name
+                      type: object
+                    type: array
+                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/controllers/cfg/controllers_cfg.go 
b/controllers/cfg/controllers_cfg.go
index 05e409cf..4f299092 100644
--- a/controllers/cfg/controllers_cfg.go
+++ b/controllers/cfg/controllers_cfg.go
@@ -65,6 +65,9 @@ func InitializeControllersCfg() (*ControllersCfg, error) {
 
 // InitializeControllersCfgAt same as InitializeControllersCfg receiving a 
path as input.
 func InitializeControllersCfgAt(configFilePath string) (*ControllersCfg, 
error) {
+       if len(configFilePath) == 0 {
+               configFilePath = defaultConfigMountPath
+       }
        controllersCfg = nil
        yamlFile, err := os.ReadFile(configFilePath)
        if err != nil {
diff --git a/controllers/platform/services/properties.go 
b/controllers/platform/services/properties.go
index 1d9e784e..c1deedcb 100644
--- a/controllers/platform/services/properties.go
+++ b/controllers/platform/services/properties.go
@@ -49,7 +49,7 @@ var (
 type serviceAppPropertyHandler struct {
        userProperties           string
        serviceHandler           PlatformServiceHandler
-       defaultMutableProperties *properties.Properties
+       defaultManagedProperties *properties.Properties
 }
 
 type ServiceAppPropertyHandler interface {
@@ -59,14 +59,14 @@ type ServiceAppPropertyHandler interface {
 
 // NewServiceAppPropertyHandler creates the default service configurations 
property handler
 // The set of properties is initialized with the operator provided immutable 
properties.
-// The set of defaultMutableProperties is initialized with the operator 
provided properties that the user might override.
+// The set of defaultManagedProperties is initialized with the operator 
provided properties that the user might override.
 func NewServiceAppPropertyHandler(serviceHandler PlatformServiceHandler) 
(ServiceAppPropertyHandler, error) {
        handler := &serviceAppPropertyHandler{}
        props, err := serviceHandler.GenerateServiceProperties()
        if err != nil {
                return nil, err
        }
-       handler.defaultMutableProperties = props
+       handler.defaultManagedProperties = props
        return handler, nil
 }
 
@@ -90,7 +90,7 @@ func (a *serviceAppPropertyHandler) Build() string {
        props = utils.NewApplicationPropertiesBuilder().
                WithInitialProperties(props).
                
WithImmutableProperties(properties.MustLoadString(immutableApplicationProperties)).
-               WithDefaultMutableProperties(a.defaultMutableProperties).
+               WithDefaultManagedProperties(a.defaultManagedProperties).
                Build()
        props.Sort()
        return props.String()
diff --git a/controllers/profiles/common/ensurer.go 
b/controllers/profiles/common/ensurer.go
index a1b8c92c..ad4558d2 100644
--- a/controllers/profiles/common/ensurer.go
+++ b/controllers/profiles/common/ensurer.go
@@ -60,7 +60,7 @@ func NewObjectEnsurer(client client.Client, creator 
ObjectCreator) ObjectEnsurer
        }
 }
 
-// NewObjectEnsurerWithPlatform see defaultObjectEnsurerWithPLatform
+// NewObjectEnsurerWithPlatform see defaultObjectEnsurerWithPlatform
 func NewObjectEnsurerWithPlatform(client client.Client, creator 
ObjectCreatorWithPlatform) ObjectEnsurerWithPlatform {
        return &defaultObjectEnsurerWithPlatform{
                c:       client,
diff --git a/controllers/profiles/common/mutate_visitors.go 
b/controllers/profiles/common/mutate_visitors.go
index 6b70bc0f..eab1944b 100644
--- a/controllers/profiles/common/mutate_visitors.go
+++ b/controllers/profiles/common/mutate_visitors.go
@@ -120,12 +120,11 @@ func ManagedPropertiesMutateVisitor(ctx context.Context, 
catalog discovery.Servi
                        if !hasKey {
                                userProperties = ""
                        }
-                       // In the future, if this needs change, instead we can 
receive an AppPropertyHandler in this mutator
-                       props, err := 
properties.NewAppPropertyHandler(workflow, plf)
+                       propertyHandler, err := 
properties.NewManagedPropertyHandler(workflow, plf)
                        if err != nil {
                                return err
                        }
-                       
managedProps.Data[workflowproj.GetManagedPropertiesFileName(workflow)] = 
props.WithUserProperties(userProperties).
+                       
managedProps.Data[workflowproj.GetManagedPropertiesFileName(workflow)] = 
propertyHandler.WithUserProperties(userProperties).
                                WithServiceDiscovery(ctx, catalog).
                                Build()
                        return nil
diff --git a/controllers/profiles/common/object_creators.go 
b/controllers/profiles/common/object_creators.go
index 61ea65db..8da5956d 100644
--- a/controllers/profiles/common/object_creators.go
+++ b/controllers/profiles/common/object_creators.go
@@ -69,7 +69,6 @@ const (
        healthStartedFailureThreshold    = 5
        healthStartedPeriodSeconds       = 15
        healthStartedInitialDelaySeconds = 10
-       defaultSchemaName                = "default"
 )
 
 // DeploymentCreator is an objectCreator for a base Kubernetes Deployments for 
profiles that need to deploy the workflow on a vanilla deployment.
@@ -307,7 +306,7 @@ func UserPropsConfigMapCreator(workflow 
*operatorapi.SonataFlow) (client.Object,
 
 // ManagedPropsConfigMapCreator creates an empty ConfigMap to hold the 
external application properties
 func ManagedPropsConfigMapCreator(workflow *operatorapi.SonataFlow, platform 
*operatorapi.SonataFlowPlatform) (client.Object, error) {
-       props, err := properties.ImmutableApplicationProperties(workflow, 
platform)
+       props, err := properties.ApplicationManagedProperties(workflow, 
platform)
        if err != nil {
                return nil, err
        }
diff --git a/controllers/profiles/common/object_creators_test.go 
b/controllers/profiles/common/object_creators_test.go
index 7a35bd48..26b54999 100644
--- a/controllers/profiles/common/object_creators_test.go
+++ b/controllers/profiles/common/object_creators_test.go
@@ -90,7 +90,6 @@ func 
Test_ensureWorkflowPropertiesConfigMapMutator_DollarReplacement(t *testing.
        err := mutateVisitorFn(managedPropsCM)()
        assert.NoError(t, err)
        assert.NotContains(t, 
managedPropsCM.Data[workflowproj.GetManagedPropertiesFileName(workflow)], 
"mp.messaging.outgoing.kogito_outgoing_stream.url")
-       // assert.Contains(t, 
managedPropsCM.Data[workflowproj.GetManagedPropertiesFileName(workflow)], 
"${kubernetes:services.v1/event-listener}")
 }
 
 func TestMergePodSpec(t *testing.T) {
diff --git a/controllers/profiles/common/properties/properties.go 
b/controllers/profiles/common/properties/knative.go
similarity index 96%
rename from controllers/profiles/common/properties/properties.go
rename to controllers/profiles/common/properties/knative.go
index ec4c3b09..195dd21f 100644
--- a/controllers/profiles/common/properties/properties.go
+++ b/controllers/profiles/common/properties/knative.go
@@ -22,7 +22,7 @@ import (
        cncfmodel "github.com/serverlessworkflow/sdk-go/v2/model"
 )
 
-// GenerateKnativeEventingWorkflowProperties returns the set of application 
properties required for the workflow to produce or consume
+// generateKnativeEventingWorkflowProperties returns the set of application 
properties required for the workflow to produce or consume
 // Knative Events.
 // Never nil.
 func generateKnativeEventingWorkflowProperties(workflow 
*operatorapi.SonataFlow) (*properties.Properties, error) {
diff --git a/controllers/profiles/common/properties/application.go 
b/controllers/profiles/common/properties/managed.go
similarity index 70%
rename from controllers/profiles/common/properties/application.go
rename to controllers/profiles/common/properties/managed.go
index 9b592608..a9da9edf 100644
--- a/controllers/profiles/common/properties/application.go
+++ b/controllers/profiles/common/properties/managed.go
@@ -42,36 +42,36 @@ var (
                "quarkus.http.host=0.0.0.0\n"+
                "quarkus.devservices.enabled=false\n"+
                "quarkus.kogito.devservices.enabled=false\n", 
constants.DefaultHTTPWorkflowPortInt)
-       _ AppPropertyHandler = &appPropertyHandler{}
+       _ ManagedPropertyHandler = &managedPropertyHandler{}
 )
 
-type AppPropertyHandler interface {
-       WithUserProperties(userProperties string) AppPropertyHandler
-       WithServiceDiscovery(ctx context.Context, catalog 
discovery.ServiceCatalog) AppPropertyHandler
+type ManagedPropertyHandler interface {
+       WithUserProperties(userProperties string) ManagedPropertyHandler
+       WithServiceDiscovery(ctx context.Context, catalog 
discovery.ServiceCatalog) ManagedPropertyHandler
        Build() string
 }
 
-type appPropertyHandler struct {
+type managedPropertyHandler struct {
        workflow                 *operatorapi.SonataFlow
        platform                 *operatorapi.SonataFlowPlatform
        catalog                  discovery.ServiceCatalog
        ctx                      context.Context
        userProperties           string
-       defaultMutableProperties *properties.Properties
+       defaultManagedProperties *properties.Properties
 }
 
-func (a *appPropertyHandler) WithUserProperties(properties string) 
AppPropertyHandler {
+func (a *managedPropertyHandler) WithUserProperties(properties string) 
ManagedPropertyHandler {
        a.userProperties = properties
        return a
 }
 
-func (a *appPropertyHandler) WithServiceDiscovery(ctx context.Context, catalog 
discovery.ServiceCatalog) AppPropertyHandler {
+func (a *managedPropertyHandler) WithServiceDiscovery(ctx context.Context, 
catalog discovery.ServiceCatalog) ManagedPropertyHandler {
        a.ctx = ctx
        a.catalog = catalog
        return a
 }
 
-func (a *appPropertyHandler) Build() string {
+func (a *managedPropertyHandler) Build() string {
        var userProps *properties.Properties
        var propErr error = nil
        if len(a.userProperties) == 0 {
@@ -87,6 +87,7 @@ func (a *appPropertyHandler) Build() string {
        // Property expansion means resolving ${} within the properties and 
environment context. Quarkus will do that in runtime.
        userProps.DisableExpansion = true
 
+       // Update discovery properties
        removeDiscoveryProperties(userProps)
        discoveryProps := properties.NewProperties()
        if a.requireServiceDiscovery() {
@@ -96,7 +97,7 @@ func (a *appPropertyHandler) Build() string {
        userProps = utils.NewApplicationPropertiesBuilder().
                WithInitialProperties(discoveryProps).
                
WithImmutableProperties(properties.MustLoadString(immutableApplicationProperties)).
-               WithDefaultMutableProperties(a.defaultMutableProperties).
+               WithDefaultManagedProperties(a.defaultManagedProperties).
                Build()
 
        return userProps.String()
@@ -104,81 +105,87 @@ func (a *appPropertyHandler) Build() string {
 
 // withKogitoServiceUrl adds the property kogitoServiceUrlProperty to the 
application properties.
 // See Service Discovery 
https://kubernetes.io/docs/concepts/services-networking/service/#dns
-func (a *appPropertyHandler) withKogitoServiceUrl() AppPropertyHandler {
+func (a *managedPropertyHandler) withKogitoServiceUrl() ManagedPropertyHandler 
{
        var kogitoServiceUrl string
        if len(a.workflow.Namespace) > 0 {
                kogitoServiceUrl = fmt.Sprintf("%s://%s.%s", 
constants.KogitoServiceURLProtocol, a.workflow.Name, a.workflow.Namespace)
        } else {
                kogitoServiceUrl = fmt.Sprintf("%s://%s", 
constants.KogitoServiceURLProtocol, a.workflow.Name)
        }
-       return a.addDefaultMutableProperty(constants.KogitoServiceURLProperty, 
kogitoServiceUrl)
+       return a.addDefaultManagedProperty(constants.KogitoServiceURLProperty, 
kogitoServiceUrl)
 }
 
 // withKafkaHealthCheckDisabled adds the property kafkaSmallRyeHealthProperty 
to the application properties.
 // See Service Discovery 
https://kubernetes.io/docs/concepts/services-networking/service/#dns
-func (a *appPropertyHandler) withKafkaHealthCheckDisabled() AppPropertyHandler 
{
-       a.addDefaultMutableProperty(
+func (a *managedPropertyHandler) withKafkaHealthCheckDisabled() 
ManagedPropertyHandler {
+       a.addDefaultManagedProperty(
                constants.DataIndexKafkaSmallRyeHealthProperty,
                "false",
        )
        return a
 }
 
-func (a *appPropertyHandler) addDefaultMutableProperty(name string, value 
string) AppPropertyHandler {
-       a.defaultMutableProperties.Set(name, value)
+func (a *managedPropertyHandler) addDefaultManagedProperty(name string, value 
string) ManagedPropertyHandler {
+       a.defaultManagedProperties.Set(name, value)
        return a
 }
 
-// NewAppPropertyHandler creates a property handler for a given workflow to 
execute in the provided platform.
+// NewManagedPropertyHandler creates a property handler for a given workflow 
to execute in the provided platform.
 // This handler is intended to build the managed application properties 
required by the workflow to execute properly together with
 // the user properties defined in the user-managed ConfigMap.
-// Note that the produced properties might vary depending on the platfom, for 
example, if the job service managed by the platform
-// a particular set of properties will be added, etc.
+// Note that the produced properties might vary depending on the platform, for 
example, if the job service managed by the platform
+// have a particular set of properties will be added, etc.
 // By default, the following properties are incorporated:
 // The set of immutable properties provided by the operator. (user can never 
change)
-// The set of defaultMutableProperties that are provided by the operator, and 
that the user cannot overwrite even if it changes
+// The set of defaultManagedProperties that are provided by the operator, and 
that the user cannot overwrite even if it changes
 // the user-managed ConfigMap. This set includes for example the required 
properties to connect with the data index and the
 // job service when any of these services are managed by the platform.
-func NewAppPropertyHandler(workflow *operatorapi.SonataFlow, platform 
*operatorapi.SonataFlowPlatform) (AppPropertyHandler, error) {
-       handler := &appPropertyHandler{
+func NewManagedPropertyHandler(workflow *operatorapi.SonataFlow, platform 
*operatorapi.SonataFlowPlatform) (ManagedPropertyHandler, error) {
+       handler := &managedPropertyHandler{
                workflow: workflow,
                platform: platform,
        }
        props := properties.NewProperties()
        props.Set(constants.KogitoUserTasksEventsEnabled, "false")
        if platform != nil {
-               p, err := 
services.GenerateDataIndexWorkflowProperties(workflow, platform)
+               p, err := resolvePlatformWorkflowProperties(platform)
                if err != nil {
                        return nil, err
                }
                props.Merge(p)
-               p, err = 
services.GenerateJobServiceWorkflowProperties(workflow, platform)
+               p, err = services.GenerateDataIndexWorkflowProperties(workflow, 
platform)
                if err != nil {
                        return nil, err
                }
                props.Merge(p)
-               p, err = generateKnativeEventingWorkflowProperties(workflow)
+               p, err = 
services.GenerateJobServiceWorkflowProperties(workflow, platform)
                if err != nil {
                        return nil, err
                }
                props.Merge(p)
-               props.Sort()
        }
-       handler.defaultMutableProperties = props
+
+       p, err := generateKnativeEventingWorkflowProperties(workflow)
+       if err != nil {
+               return nil, err
+       }
+       props.Merge(p)
+       props.Sort()
+
+       handler.defaultManagedProperties = props
        return handler.withKogitoServiceUrl(), nil
 }
 
-// ImmutableApplicationProperties immutable default application properties 
that can be used with any workflow based on Quarkus.
-// Alias for NewAppPropertyHandler(workflow).Build()
-func ImmutableApplicationProperties(workflow *operatorapi.SonataFlow, platform 
*operatorapi.SonataFlowPlatform) (string, error) {
-       p, err := NewAppPropertyHandler(workflow, platform)
+// ApplicationManagedProperties immutable default application properties that 
can be used with any workflow based on Quarkus.
+// Alias for NewManagedPropertyHandler(workflow).Build()
+func ApplicationManagedProperties(workflow *operatorapi.SonataFlow, platform 
*operatorapi.SonataFlowPlatform) (string, error) {
+       p, err := NewManagedPropertyHandler(workflow, platform)
        if err != nil {
                return "", err
        }
        return p.Build(), nil
-
 }
 
-func (a *appPropertyHandler) requireServiceDiscovery() bool {
+func (a *managedPropertyHandler) requireServiceDiscovery() bool {
        return a.ctx != nil && a.catalog != nil
 }
diff --git a/controllers/profiles/common/properties/application_test.go 
b/controllers/profiles/common/properties/managed_test.go
similarity index 97%
rename from controllers/profiles/common/properties/application_test.go
rename to controllers/profiles/common/properties/managed_test.go
index 93700baf..d27d69e1 100644
--- a/controllers/profiles/common/properties/application_test.go
+++ b/controllers/profiles/common/properties/managed_test.go
@@ -111,7 +111,7 @@ func (c *mockCatalogService) Query(ctx context.Context, uri 
discovery.ResourceUr
 
 func Test_appPropertyHandler_WithKogitoServiceUrl(t *testing.T) {
        workflow := test.GetBaseSonataFlow("default")
-       props, err := ImmutableApplicationProperties(workflow, nil)
+       props, err := ApplicationManagedProperties(workflow, nil)
        assert.NoError(t, err)
        assert.Contains(t, props, constants.KogitoServiceURLProperty)
        assert.Contains(t, props, 
"http://"+workflow.Name+"."+workflow.Namespace)
@@ -121,11 +121,11 @@ func 
Test_appPropertyHandler_WithUserPropertiesWithNoUserOverrides(t *testing.T)
        //just add some user provided properties, no overrides.
        userProperties := "property1=value1\nproperty2=value2"
        workflow := test.GetBaseSonataFlow("default")
-       props, err := NewAppPropertyHandler(workflow, nil)
+       props, err := NewManagedPropertyHandler(workflow, nil)
        assert.NoError(t, err)
        generatedProps, propsErr := 
properties.LoadString(props.WithUserProperties(userProperties).Build())
        assert.NoError(t, propsErr)
-       assert.Equal(t, 6, len(generatedProps.Keys()))
+       assert.Equal(t, 7, len(generatedProps.Keys()))
        assert.NotContains(t, "property1", generatedProps.Keys())
        assert.NotContains(t, "property2", generatedProps.Keys())
        assert.Equal(t, "http://greeting.default";, 
generatedProps.GetString("kogito.service.url", ""))
@@ -149,7 +149,7 @@ func 
Test_appPropertyHandler_WithUserPropertiesWithServiceDiscovery(t *testing.T
        userProperties = userProperties + 
"broker2=${knative:brokers.v1.eventing.knative.dev/my-kn-broker2}\n"
 
        workflow := test.GetBaseSonataFlow(defaultNamespace)
-       props, err := NewAppPropertyHandler(workflow, nil)
+       props, err := NewManagedPropertyHandler(workflow, nil)
        assert.NoError(t, err)
        generatedProps, propsErr := properties.LoadString(props.
                WithUserProperties(userProperties).
@@ -157,7 +157,7 @@ func 
Test_appPropertyHandler_WithUserPropertiesWithServiceDiscovery(t *testing.T
                Build())
        generatedProps.DisableExpansion = true
        assert.NoError(t, propsErr)
-       assert.Equal(t, 20, len(generatedProps.Keys()))
+       assert.Equal(t, 21, len(generatedProps.Keys()))
        assert.NotContains(t, "property1", generatedProps.Keys())
        assert.NotContains(t, "property2", generatedProps.Keys())
        assertHasProperty(t, generatedProps, "service1", myService1Address)
@@ -186,12 +186,6 @@ func 
Test_appPropertyHandler_WithUserPropertiesWithServiceDiscovery(t *testing.T
        assertHasProperty(t, generatedProps, 
constants.KogitoUserTasksEventsEnabled, "false")
 }
 
-func assertHasProperty(t *testing.T, props *properties.Properties, 
expectedProperty string, expectedValue string) {
-       value, ok := props.Get(expectedProperty)
-       assert.True(t, ok, "Property %s, is not present as expected.", 
expectedProperty)
-       assert.Equal(t, expectedValue, value, "Expected value for property: %s, 
is: %s but current value is: %s", expectedProperty, expectedValue, value)
-}
-
 func Test_appPropertyHandler_WithServicesWithUserOverrides(t *testing.T) {
        //try to override kogito.service.url and quarkus.http.port
        userProperties := 
"property1=value1\nproperty2=value2\nquarkus.http.port=9090\nkogito.service.url=http://myUrl.override.com\nquarkus.http.port=9090";
@@ -214,7 +208,7 @@ func 
Test_appPropertyHandler_WithServicesWithUserOverrides(t *testing.T) {
 
        services.SetServiceUrlsInWorkflowStatus(platform, workflow)
        assert.Nil(t, workflow.Status.Services)
-       props, err := NewAppPropertyHandler(workflow, platform)
+       props, err := NewManagedPropertyHandler(workflow, platform)
        assert.NoError(t, err)
        generatedProps, propsErr := 
properties.LoadString(props.WithUserProperties(userProperties).Build())
        assert.NoError(t, propsErr)
@@ -243,7 +237,7 @@ func 
Test_appPropertyHandler_WithServicesWithUserOverrides(t *testing.T) {
        assert.NotNil(t, workflow.Status.Services)
        assert.NotNil(t, workflow.Status.Services.JobServiceRef)
        assert.NotNil(t, workflow.Status.Services.DataIndexRef)
-       props, err = NewAppPropertyHandler(workflow, platform)
+       props, err = NewManagedPropertyHandler(workflow, platform)
        assert.NoError(t, err)
        generatedProps, propsErr = 
properties.LoadString(props.WithUserProperties(userProperties).Build())
        assert.NoError(t, propsErr)
@@ -270,7 +264,7 @@ func 
Test_appPropertyHandler_WithServicesWithUserOverrides(t *testing.T) {
        assert.NotNil(t, workflow.Status.Services)
        assert.NotNil(t, workflow.Status.Services.JobServiceRef)
        assert.Nil(t, workflow.Status.Services.DataIndexRef)
-       props, err = NewAppPropertyHandler(workflow, platform)
+       props, err = NewManagedPropertyHandler(workflow, platform)
        assert.NoError(t, err)
        generatedProps, propsErr = 
properties.LoadString(props.WithUserProperties(userProperties).Build())
        assert.NoError(t, propsErr)
@@ -290,7 +284,7 @@ func 
Test_appPropertyHandler_WithServicesWithUserOverrides(t *testing.T) {
        platform.Spec.Services.JobService.Enabled = nil
        services.SetServiceUrlsInWorkflowStatus(platform, workflow)
        assert.Nil(t, workflow.Status.Services)
-       props, err = NewAppPropertyHandler(workflow, platform)
+       props, err = NewManagedPropertyHandler(workflow, platform)
        assert.NoError(t, err)
        generatedProps, propsErr = 
properties.LoadString(props.WithUserProperties(userProperties).Build())
        assert.NoError(t, propsErr)
@@ -317,7 +311,7 @@ var _ = Describe("Platform properties", func() {
                        DescribeTable("only job services when the spec",
                                func(wf *operatorapi.SonataFlow, plfm 
*operatorapi.SonataFlowPlatform, expectedProperties *properties.Properties) {
                                        
services.SetServiceUrlsInWorkflowStatus(plfm, wf)
-                                       handler, err := 
NewAppPropertyHandler(wf, plfm)
+                                       handler, err := 
NewManagedPropertyHandler(wf, plfm)
                                        Expect(err).NotTo(HaveOccurred())
                                        p, err := 
properties.LoadString(handler.Build())
                                        Expect(err).NotTo(HaveOccurred())
@@ -365,7 +359,7 @@ var _ = Describe("Platform properties", func() {
                        DescribeTable("only data index service when the spec",
                                func(wf *operatorapi.SonataFlow, plfm 
*operatorapi.SonataFlowPlatform, expectedProperties *properties.Properties) {
                                        
services.SetServiceUrlsInWorkflowStatus(plfm, wf)
-                                       handler, err := 
NewAppPropertyHandler(wf, plfm)
+                                       handler, err := 
NewManagedPropertyHandler(wf, plfm)
                                        Expect(err).NotTo(HaveOccurred())
                                        p, err := 
properties.LoadString(handler.Build())
                                        Expect(err).NotTo(HaveOccurred())
@@ -412,7 +406,7 @@ var _ = Describe("Platform properties", func() {
 
                        DescribeTable("both Data Index and Job Services are 
available and", func(wf *operatorapi.SonataFlow, plfm 
*operatorapi.SonataFlowPlatform, expectedProperties *properties.Properties) {
                                services.SetServiceUrlsInWorkflowStatus(plfm, 
wf)
-                               handler, err := NewAppPropertyHandler(wf, plfm)
+                               handler, err := NewManagedPropertyHandler(wf, 
plfm)
                                Expect(err).NotTo(HaveOccurred())
                                p, err := properties.LoadString(handler.Build())
                                Expect(err).NotTo(HaveOccurred())
diff --git a/controllers/profiles/common/properties/platform.go 
b/controllers/profiles/common/properties/platform.go
new file mode 100644
index 00000000..2b5704f4
--- /dev/null
+++ b/controllers/profiles/common/properties/platform.go
@@ -0,0 +1,87 @@
+// 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 properties
+
+import (
+       "context"
+
+       operatorapi 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       "github.com/apache/incubator-kie-kogito-serverless-operator/log"
+       "github.com/apache/incubator-kie-kogito-serverless-operator/utils"
+       "github.com/magiconair/properties"
+       v1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/errors"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/types"
+       "k8s.io/klog/v2"
+)
+
+func resolvePlatformWorkflowProperties(platform 
*operatorapi.SonataFlowPlatform) (*properties.Properties, error) {
+       props := properties.NewProperties()
+
+       if platform.Spec.Properties == nil {
+               return props, nil
+       }
+
+       for _, propVar := range platform.Spec.Properties.Flow {
+               if len(propVar.Value) > 0 {
+                       props.Set(propVar.Name, propVar.Value)
+               } else if propVar.ValueFrom != nil {
+                       val, err := getPropVarRefValue(propVar.ValueFrom, 
platform.Namespace)
+                       if err != nil {
+                               return nil, err
+                       }
+                       props.Set(propVar.Name, val)
+               }
+       }
+
+       return props, nil
+}
+
+func getPropVarRefValue(from *operatorapi.PropertyVarSource, namespace string) 
(string, error) {
+       // same order as k8s api (we try to fetch first a secret)
+       if from.SecretKeyRef != nil {
+               secret := &v1.Secret{
+                       ObjectMeta: metav1.ObjectMeta{Namespace: namespace, 
Name: from.SecretKeyRef.Name},
+               }
+               err := utils.GetClient().Get(context.TODO(), 
types.NamespacedName{Namespace: secret.Namespace, Name: secret.Name}, secret)
+               if err != nil && !errors.IsNotFound(err) {
+                       return "", err
+               }
+               if data, ok := secret.Data[from.SecretKeyRef.Key]; ok {
+                       return string(data), nil
+               }
+               if from.SecretKeyRef.Optional == utils.Pbool(false) {
+                       klog.V(log.D).InfoS("Key not found in secret", "Key", 
from.SecretKeyRef.Key)
+               }
+       }
+       if from.ConfigMapKeyRef != nil {
+               cm := &v1.ConfigMap{
+                       ObjectMeta: metav1.ObjectMeta{Namespace: namespace, 
Name: from.ConfigMapKeyRef.Name},
+               }
+               err := utils.GetClient().Get(context.TODO(), 
types.NamespacedName{Namespace: cm.Namespace, Name: cm.Name}, cm)
+               if err != nil && !errors.IsNotFound(err) {
+                       return "", err
+               }
+               if data, ok := cm.Data[from.ConfigMapKeyRef.Key]; ok {
+                       return data, nil
+               }
+               if from.ConfigMapKeyRef.Optional == utils.Pbool(false) {
+                       klog.V(log.D).InfoS("Key not found in configMap", 
"Key", from.ConfigMapKeyRef.Key)
+               }
+       }
+
+       return "", nil
+}
diff --git a/controllers/profiles/common/properties/platform_test.go 
b/controllers/profiles/common/properties/platform_test.go
new file mode 100644
index 00000000..52363661
--- /dev/null
+++ b/controllers/profiles/common/properties/platform_test.go
@@ -0,0 +1,119 @@
+// 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 properties
+
+import (
+       "testing"
+
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       "github.com/apache/incubator-kie-kogito-serverless-operator/test"
+       "github.com/apache/incubator-kie-kogito-serverless-operator/utils"
+       "github.com/stretchr/testify/assert"
+       v1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func Test_resolvePlatformWorkflowProperties(t *testing.T) {
+       secret := &v1.Secret{
+               ObjectMeta: metav1.ObjectMeta{Name: "secretPlatformTest", 
Namespace: t.Name()},
+               Data:       map[string][]byte{"my-key": []byte("secret")},
+       }
+       cm := &v1.ConfigMap{
+               ObjectMeta: metav1.ObjectMeta{Name: "configMapPlatformTest", 
Namespace: t.Name()},
+               Data:       map[string]string{"my-key": "value"},
+       }
+
+       platform := test.GetBasePlatform()
+       platform.Namespace = t.Name()
+       platform.Spec.Properties = &v1alpha08.PropertyPlatformSpec{
+               Flow: []v1alpha08.PropertyVar{
+                       {
+                               Name:  "quarkus.log.category",
+                               Value: "DEBUG",
+                       },
+                       {
+                               Name: "quarkus.custom.property",
+                               ValueFrom: &v1alpha08.PropertyVarSource{
+                                       ConfigMapKeyRef: 
&v1.ConfigMapKeySelector{
+                                               Key:                  "my-key",
+                                               LocalObjectReference: 
v1.LocalObjectReference{Name: "configMapPlatformTest"},
+                                       },
+                               },
+                       },
+                       {
+                               Name: "quarkus.custom.secret",
+                               ValueFrom: &v1alpha08.PropertyVarSource{
+                                       SecretKeyRef: &v1.SecretKeySelector{
+                                               Key:                  "my-key",
+                                               LocalObjectReference: 
v1.LocalObjectReference{Name: "secretPlatformTest"},
+                                       },
+                               },
+                       },
+               },
+       }
+
+       client := 
test.NewSonataFlowClientBuilder().WithRuntimeObjects(platform, secret, 
cm).WithStatusSubresource(platform).Build()
+       utils.SetClient(client)
+
+       props, err := resolvePlatformWorkflowProperties(platform)
+       assert.NoError(t, err)
+       assert.NotNil(t, props)
+
+       assertHasProperty(t, props, "quarkus.log.category", "DEBUG")
+       assertHasProperty(t, props, "quarkus.custom.property", "value")
+       assertHasProperty(t, props, "quarkus.custom.secret", "secret")
+}
+
+func Test_resolvePlatformWorkflowProperties_RefNotFound(t *testing.T) {
+       platform := test.GetBasePlatform()
+       platform.Namespace = t.Name()
+       platform.Spec.Properties = &v1alpha08.PropertyPlatformSpec{
+               Flow: []v1alpha08.PropertyVar{
+                       {
+                               Name:  "quarkus.log.category",
+                               Value: "DEBUG",
+                       },
+                       {
+                               Name: "quarkus.custom.property",
+                               ValueFrom: &v1alpha08.PropertyVarSource{
+                                       ConfigMapKeyRef: 
&v1.ConfigMapKeySelector{
+                                               Key:                  "my-key",
+                                               LocalObjectReference: 
v1.LocalObjectReference{Name: "configMapPlatformTest"},
+                                       },
+                               },
+                       },
+                       {
+                               Name: "quarkus.custom.secret",
+                               ValueFrom: &v1alpha08.PropertyVarSource{
+                                       SecretKeyRef: &v1.SecretKeySelector{
+                                               Key:                  "my-key",
+                                               LocalObjectReference: 
v1.LocalObjectReference{Name: "secretPlatformTest"},
+                                       },
+                               },
+                       },
+               },
+       }
+
+       client := 
test.NewSonataFlowClientBuilder().WithRuntimeObjects(platform).WithStatusSubresource(platform).Build()
+       utils.SetClient(client)
+
+       props, err := resolvePlatformWorkflowProperties(platform)
+       assert.NoError(t, err)
+       assert.NotNil(t, props)
+
+       assertHasProperty(t, props, "quarkus.log.category", "DEBUG")
+       assertHasProperty(t, props, "quarkus.custom.property", "")
+       assertHasProperty(t, props, "quarkus.custom.secret", "")
+}
diff --git a/controllers/profiles/common/properties/properties_test.go 
b/controllers/profiles/common/properties/properties_test.go
new file mode 100644
index 00000000..81c91ef8
--- /dev/null
+++ b/controllers/profiles/common/properties/properties_test.go
@@ -0,0 +1,28 @@
+// 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 properties
+
+import (
+       "testing"
+
+       "github.com/magiconair/properties"
+       "github.com/stretchr/testify/assert"
+)
+
+func assertHasProperty(t *testing.T, props *properties.Properties, 
expectedProperty string, expectedValue string) {
+       value, ok := props.Get(expectedProperty)
+       assert.True(t, ok, "Property %s, is not present as expected.", 
expectedProperty)
+       assert.Equal(t, expectedValue, value, "Expected value for property: %s, 
is: %s but current value is: %s", expectedProperty, expectedValue, value)
+}
diff --git a/hack/local/run-operator.sh b/hack/local/run-operator.sh
index 628bee27..5cb8a73c 100755
--- a/hack/local/run-operator.sh
+++ b/hack/local/run-operator.sh
@@ -18,11 +18,13 @@
 
 # Runs the operator locally via go main
 
+kubectl delete --ignore-not-found=true -f 
./bundle/manifests/sonataflow.org_sonataflowclusterplatforms.yaml
 kubectl delete --ignore-not-found=true -f 
./bundle/manifests/sonataflow.org_sonataflowplatforms.yaml
 kubectl delete --ignore-not-found=true -f 
./bundle/manifests/sonataflow.org_sonataflowbuilds.yaml
 kubectl delete --ignore-not-found=true -f 
./bundle/manifests/sonataflow.org_sonataflows.yaml
 
 kubectl create -f ./bundle/manifests/sonataflow.org_sonataflowplatforms.yaml
+kubectl create -f 
./bundle/manifests/sonataflow.org_sonataflowclusterplatforms.yaml
 kubectl create -f ./bundle/manifests/sonataflow.org_sonataflowbuilds.yaml
 kubectl create -f ./bundle/manifests/sonataflow.org_sonataflows.yaml
 kubectl apply -f 
./bundle/manifests/sonataflow-operator-builder-config_v1_configmap.yaml
diff --git a/main.go b/main.go
index 2e39652c..535a3ed0 100644
--- a/main.go
+++ b/main.go
@@ -67,12 +67,14 @@ func main() {
        var metricsAddr string
        var enableLeaderElection bool
        var probeAddr string
+       var controllerCfgPath string
        klog.InitFlags(nil)
        flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The 
address the metric endpoint binds to.")
        flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The 
address the probe endpoint binds to.")
        flag.BoolVar(&enableLeaderElection, "leader-elect", false,
                "Enable leader election for controller manager. "+
                        "Enabling this will ensure there is only one active 
controller manager.")
+       flag.StringVar(&controllerCfgPath, "controller-cfg-path", "", "The 
controller config file path.")
        flag.Parse()
 
        ctrl.SetLogger(klogr.New().WithName(controllers.ComponentName))
@@ -90,10 +92,12 @@ func main() {
                os.Exit(1)
        }
 
+       // Set global assessors
        utils.SetIsOpenShift(mgr.GetConfig())
+       utils.SetClient(mgr.GetClient())
 
        // Fail fast, we can change this behavior in the future to read from 
defaults instead.
-       if _, err = cfg.InitializeControllersCfg(); err != nil {
+       if _, err = cfg.InitializeControllersCfgAt(controllerCfgPath); err != 
nil {
                klog.V(log.E).ErrorS(err, "unable to read controllers 
configuration file")
                os.Exit(1)
        }
diff --git a/operator.yaml b/operator.yaml
index ef09c6cb..14dfec14 100644
--- a/operator.yaml
+++ b/operator.yaml
@@ -915,7 +915,7 @@ spec:
               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
+                  for platform services and SonataFlow instances that don't 
provide
                   one of their own.
                 maxProperties: 1
                 properties:
@@ -969,6 +969,78 @@ spec:
                     - secretRef
                     type: object
                 type: object
+              properties:
+                description: "Properties defines the property set for a given 
actor
+                  in the current context. For example, the workflow managed 
properties.
+                  One can define here a set of properties for SonataFlow 
deployments
+                  that will be reused across every workflow deployment. \n 
These properties
+                  MAY NOT be propagated to a SonataFlowClusterPlatform since 
PropertyVarSource
+                  can only refer local context sources."
+                properties:
+                  flow:
+                    description: Properties that will be added to the 
SonataFlow managed
+                      configMaps in the current context.
+                    items:
+                      description: PropertyVar is the entry for a property set 
derived
+                        from the Kubernetes API EnvVar. Note that the name 
doesn't
+                        have to match C_IDENTIFIER.
+                      properties:
+                        name:
+                          description: The property name
+                          type: string
+                        value:
+                          description: Defaults to "".
+                          type: string
+                        valueFrom:
+                          description: Source for the property's value. Cannot 
be
+                            used if value is not empty.
+                          properties:
+                            configMapKeyRef:
+                              description: Selects a key of a ConfigMap.
+                              properties:
+                                key:
+                                  description: The key to select.
+                                  type: string
+                                name:
+                                  description: 'Name of the referent. More 
info: 
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                    TODO: Add other useful fields. apiVersion, 
kind,
+                                    uid?'
+                                  type: string
+                                optional:
+                                  description: Specify whether the ConfigMap 
or its
+                                    key must be defined
+                                  type: boolean
+                              required:
+                              - key
+                              type: object
+                              x-kubernetes-map-type: atomic
+                            secretKeyRef:
+                              description: Selects a key of a secret in the 
flow's
+                                namespace
+                              properties:
+                                key:
+                                  description: The key of the secret to select 
from.  Must
+                                    be a valid secret key.
+                                  type: string
+                                name:
+                                  description: 'Name of the referent. More 
info: 
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                    TODO: Add other useful fields. apiVersion, 
kind,
+                                    uid?'
+                                  type: string
+                                optional:
+                                  description: Specify whether the Secret or 
its key
+                                    must be defined
+                                  type: boolean
+                              required:
+                              - key
+                              type: object
+                              x-kubernetes-map-type: atomic
+                          type: object
+                      required:
+                      - name
+                      type: object
+                    type: array
+                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/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform.yaml 
b/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform.yaml
index 23af744d..39c5f1c1 100644
--- a/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform.yaml
+++ b/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform.yaml
@@ -20,6 +20,10 @@ kind: SonataFlowPlatform
 metadata:
   name: sonataflow-platform
 spec:
+  properties:
+    flow:
+      - name: quarkus.log.level
+        value: INFO
   build:
     config:
       registry:
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 2de0372b..f61974cb 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-persistence-jdbc:999-SNAPSHOT,io.quarkus:quarkus-jdbc-postgresql:3.2.9.Final,io.quarkus:quarkus-agroal:3.2.9.Final
+        value: 
org.kie:kie-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 e342b1c1..ddae61a8 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
@@ -31,7 +31,7 @@ spec:
     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
+        value: 
org.kie:kie-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 66eec55a..0d1088fd 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
@@ -31,7 +31,7 @@ spec:
     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
+        value: 
org.kie:kie-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 f61f4a84..0b076a86 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
@@ -31,7 +31,7 @@ spec:
     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
+        value: 
org.kie:kie-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 6dc311d6..3e24fe66 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
@@ -31,7 +31,7 @@ spec:
     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
+        value: 
org.kie:kie-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/utils/client.go b/utils/client.go
new file mode 100644
index 00000000..9b1cd085
--- /dev/null
+++ b/utils/client.go
@@ -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.
+
+package utils
+
+import "sigs.k8s.io/controller-runtime/pkg/client"
+
+var k8sClient client.Client
+
+// TODO: consider refactor the internals as we progress adding features to 
rely on this client instead of passing it through all the functions
+
+// GetClient default client created by the main operator's thread.
+// It's safe to use since it's set when the operator main function runs.
+func GetClient() client.Client {
+       return k8sClient
+}
+
+// SetClient is meant for internal use only. Don't call it!
+func SetClient(client client.Client) {
+       k8sClient = client
+}
diff --git a/utils/properties.go b/utils/properties.go
index 382dc1b5..97c3c54c 100644
--- a/utils/properties.go
+++ b/utils/properties.go
@@ -26,7 +26,7 @@ import (
 type ApplicationPropertiesBuilder interface {
        WithInitialProperties(initialProperties *properties.Properties) 
ApplicationPropertiesBuilder
        WithImmutableProperties(immutableProperties *properties.Properties) 
ApplicationPropertiesBuilder
-       WithDefaultMutableProperties(defaultMutableProperties 
*properties.Properties) ApplicationPropertiesBuilder
+       WithDefaultManagedProperties(defaultManagedProperties 
*properties.Properties) ApplicationPropertiesBuilder
        BuildAsString() string
        Build() *properties.Properties
 }
@@ -34,7 +34,7 @@ type ApplicationPropertiesBuilder interface {
 type applicationPropertiesBuilder struct {
        initialProperties        *properties.Properties
        immutableProperties      *properties.Properties
-       defaultMutableProperties *properties.Properties
+       defaultManagedProperties *properties.Properties
 }
 
 func (a *applicationPropertiesBuilder) WithInitialProperties(initialProperties 
*properties.Properties) ApplicationPropertiesBuilder {
@@ -47,8 +47,8 @@ func (a *applicationPropertiesBuilder) 
WithImmutableProperties(immutableProperti
        return a
 }
 
-func (a *applicationPropertiesBuilder) 
WithDefaultMutableProperties(defaultMutableProperties *properties.Properties) 
ApplicationPropertiesBuilder {
-       a.defaultMutableProperties = defaultMutableProperties
+func (a *applicationPropertiesBuilder) 
WithDefaultManagedProperties(defaultManagedProperties *properties.Properties) 
ApplicationPropertiesBuilder {
+       a.defaultManagedProperties = defaultManagedProperties
        return a
 }
 
@@ -67,14 +67,14 @@ func (a *applicationPropertiesBuilder) Build() 
*properties.Properties {
        // Property expansion means resolving ${} within the properties and 
environment context. Quarkus will do that in runtime.
        props.DisableExpansion = true
 
-       if a.defaultMutableProperties != nil {
-               defaultMutableProps := a.defaultMutableProperties
-               for _, k := range defaultMutableProps.Keys() {
+       if a.defaultManagedProperties != nil {
+               defaultManagedProperties := a.defaultManagedProperties
+               for _, k := range defaultManagedProperties.Keys() {
                        if _, ok := props.Get(k); ok {
-                               defaultMutableProps.Delete(k)
+                               defaultManagedProperties.Delete(k)
                        }
                }
-               props.Merge(defaultMutableProps)
+               props.Merge(defaultManagedProperties)
        }
 
        if a.immutableProperties != nil {


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

Reply via email to