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 20694d65 [KOGITO-9972] Add SonataFlowClusterPlatform CRD & Controller 
(#345)
20694d65 is described below

commit 20694d651e755ff43e332b276c524b3ceca96cb8
Author: Tommy Hughes IV <[email protected]>
AuthorDate: Thu Feb 1 09:55:24 2024 -0600

    [KOGITO-9972] Add SonataFlowClusterPlatform CRD & Controller (#345)
    
    * SonataFlowClusterPlatform CRD & Controller
    
    Signed-off-by: Tommy Hughes <[email protected]>
    
    * service constants cleanup
    
    Signed-off-by: Tommy Hughes <[email protected]>
    
    * clusterplatform api descrip change
    
    Signed-off-by: Tommy Hughes <[email protected]>
    
    ---------
    
    Signed-off-by: Tommy Hughes <[email protected]>
---
 PROJECT                                            |   8 +
 api/v1alpha08/sonataflowclusterplatform_types.go   | 100 ++++++++++
 .../sonataflowclusterplatform_types_support.go     |  34 ++++
 api/v1alpha08/sonataflowplatform_services_types.go |   8 +-
 api/v1alpha08/sonataflowplatform_types.go          |  37 +++-
 api/v1alpha08/zz_generated.deepcopy.go             | 178 +++++++++++++++++-
 ...e_rbac.authorization.k8s.io_v1_clusterrole.yaml |  27 +++
 .../sonataflow-operator.clusterserviceversion.yaml |  56 ++++++
 .../sonataflow.org_sonataflowclusterplatforms.yaml | 120 ++++++++++++
 .../sonataflow.org_sonataflowplatforms.yaml        |  68 +++++--
 .../sonataflow.org_sonataflowclusterplatforms.yaml | 115 ++++++++++++
 .../bases/sonataflow.org_sonataflowplatforms.yaml  |  68 +++++--
 config/crd/kustomization.yaml                      |   3 +
 .../cainjection_in_sonataflowclusterplatforms.yaml |   7 +
 .../webhook_in_sonataflowclusterplatforms.yaml     |  16 ++
 .../sonataflow-operator.clusterserviceversion.yaml |  17 ++
 config/rbac/role.yaml                              |  26 +++
 .../sonataflowclusterplatform_editor_role.yaml     |  31 +++
 ...lusterplatform_viewer_cluster_role_binding.yaml |  13 ++
 .../sonataflowclusterplatform_viewer_role.yaml     |  27 +++
 config/samples/kustomization.yaml                  |   1 +
 ...ow.org_v1alpha08_sonataflowclusterplatform.yaml |   8 +
 controllers/clusterplatform/action.go              |  50 +++++
 controllers/clusterplatform/clusterplatform.go     | 117 ++++++++++++
 controllers/clusterplatform/initialize.go          | 118 ++++++++++++
 controllers/platform/k8s.go                        |  39 ++--
 controllers/platform/platformutils.go              |  16 +-
 controllers/platform/services/properties.go        |   7 +-
 .../platform/services/properties_services_test.go  |  18 ++
 controllers/platform/services/services.go          | 159 ++++++++++++++--
 .../profiles/common/constants/platform_services.go |   3 +
 .../profiles/common/properties/application_test.go |  11 +-
 .../sonataflowclusterplatform_controller.go        | 175 +++++++++++++++++
 controllers/sonataflowplatform_controller.go       | 120 ++++++++++--
 controllers/sonataflowplatform_controller_test.go  | 143 ++++++++++++--
 main.go                                            |  10 +
 operator.yaml                                      | 209 +++++++++++++++++++--
 ...ow.org_v1alpha08_sonataflowclusterplatform.yaml |  25 +++
 test/yaml.go                                       |  48 ++++-
 39 files changed, 2119 insertions(+), 117 deletions(-)

diff --git a/PROJECT b/PROJECT
index f32f2b3c..6ef3543e 100644
--- a/PROJECT
+++ b/PROJECT
@@ -34,4 +34,12 @@ resources:
   kind: SonataFlowPlatform
   path: 
github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08
   version: v1alpha08
+- api:
+    crdVersion: v1
+  controller: true
+  domain: org
+  group: sonataflow
+  kind: SonataFlowClusterPlatform
+  path: 
github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08
+  version: v1alpha08
 version: "3"
diff --git a/api/v1alpha08/sonataflowclusterplatform_types.go 
b/api/v1alpha08/sonataflowclusterplatform_types.go
new file mode 100644
index 00000000..4016e507
--- /dev/null
+++ b/api/v1alpha08/sonataflowclusterplatform_types.go
@@ -0,0 +1,100 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 (
+       "github.com/apache/incubator-kie-kogito-serverless-operator/api"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+const (
+       // SonataFlowClusterPlatformKind is the Kind name of the 
SonataFlowClusterPlatform CR
+       SonataFlowClusterPlatformKind string = "SonataFlowClusterPlatform"
+       PlatformNotFoundReason        string = "PlatformNotFound"
+)
+
+// SonataFlowClusterPlatformSpec defines the desired state of 
SonataFlowClusterPlatform
+type SonataFlowClusterPlatformSpec struct {
+       PlatformRef SonataFlowPlatformRef `json:"platformRef"`
+}
+
+// SonataFlowPlatformRef defines which existing SonataFlowPlatform's 
supporting services should be used cluster-wide.
+type SonataFlowPlatformRef struct {
+       // Name of the SonataFlowPlatform
+       
//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Platform_Name"
+       Name string `json:"name"`
+       // Namespace of the SonataFlowPlatform
+       
//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Platform_NS"
+       Namespace string `json:"namespace"`
+}
+
+// SonataFlowClusterPlatformStatus defines the observed state of 
SonataFlowClusterPlatform
+type SonataFlowClusterPlatformStatus struct {
+       api.Status `json:",inline"`
+       // Version the operator version controlling this ClusterPlatform
+       
//+operator-sdk:csv:customresourcedefinitions:type=status,displayName="version"
+       Version string `json:"version,omitempty"`
+}
+
+func (in *SonataFlowClusterPlatformStatus) GetTopLevelConditionType() 
api.ConditionType {
+       return api.SucceedConditionType
+}
+
+func (in *SonataFlowClusterPlatformStatus) IsReady() bool {
+       return in.GetTopLevelCondition().IsTrue()
+}
+
+func (in *SonataFlowClusterPlatformStatus) GetTopLevelCondition() 
*api.Condition {
+       return in.GetCondition(in.GetTopLevelConditionType())
+}
+
+func (in *SonataFlowClusterPlatformStatus) Manager() api.ConditionsManager {
+       return api.NewConditionManager(in, api.SucceedConditionType)
+}
+
+func (in *SonataFlowClusterPlatformStatus) IsDuplicated() bool {
+       cond := in.GetTopLevelCondition()
+       return cond.IsFalse() && cond.Reason == PlatformDuplicatedReason
+}
+
+// SonataFlowClusterPlatform is the Schema for the sonataflowclusterplatforms 
API
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+// +kubebuilder:resource:scope=Cluster
+// 
+kubebuilder:printcolumn:name="Platform_Name",type=string,JSONPath=`.spec.platformRef.name`
+// 
+kubebuilder:printcolumn:name="Platform_NS",type=string,JSONPath=`.spec.platformRef.namespace`
+// 
+kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=='Succeed')].status`
+// 
+kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=='Succeed')].reason`
+type SonataFlowClusterPlatform struct {
+       metav1.TypeMeta   `json:",inline"`
+       metav1.ObjectMeta `json:"metadata,omitempty"`
+
+       Spec   SonataFlowClusterPlatformSpec   `json:"spec,omitempty"`
+       Status SonataFlowClusterPlatformStatus `json:"status,omitempty"`
+}
+
+//+kubebuilder:object:root=true
+
+// SonataFlowClusterPlatformList contains a list of SonataFlowClusterPlatform
+type SonataFlowClusterPlatformList struct {
+       metav1.TypeMeta `json:",inline"`
+       metav1.ListMeta `json:"metadata,omitempty"`
+       Items           []SonataFlowClusterPlatform `json:"items"`
+}
+
+func init() {
+       SchemeBuilder.Register(&SonataFlowClusterPlatform{}, 
&SonataFlowClusterPlatformList{})
+}
diff --git a/api/v1alpha08/sonataflowclusterplatform_types_support.go 
b/api/v1alpha08/sonataflowclusterplatform_types_support.go
new file mode 100644
index 00000000..8c126555
--- /dev/null
+++ b/api/v1alpha08/sonataflowclusterplatform_types_support.go
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 (
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// NewSonataFlowClusterPlatformList returns an empty list of ClusterPlatform 
objects
+func NewSonataFlowClusterPlatformList() SonataFlowClusterPlatformList {
+       return SonataFlowClusterPlatformList{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: GroupVersion.String(),
+                       Kind:       SonataFlowClusterPlatformKind,
+               },
+       }
+}
diff --git a/api/v1alpha08/sonataflowplatform_services_types.go 
b/api/v1alpha08/sonataflowplatform_services_types.go
index 4c3a6610..9ecad3de 100644
--- a/api/v1alpha08/sonataflowplatform_services_types.go
+++ b/api/v1alpha08/sonataflowplatform_services_types.go
@@ -14,12 +14,12 @@
 
 package v1alpha08
 
-// ServicesPlatformSpec describes the desired service configuration for "prod" 
workflows.
+// ServicesPlatformSpec describes the desired service configuration for 
workflows without the `sonataflow.org/profile: dev` annotation.
 type ServicesPlatformSpec struct {
-       // Deploys the Data Index service for use by "prod" profile workflows.
+       // Deploys the Data Index service for use by workflows without the 
`sonataflow.org/profile: dev` annotation.
        // +optional
        DataIndex *ServiceSpec `json:"dataIndex,omitempty"`
-       // Deploys the Job service for use by "prod" profile workflows.
+       // Deploys the Job service for use by workflows without the 
`sonataflow.org/profile: dev` annotation.
        // +optional
        JobService *ServiceSpec `json:"jobService,omitempty"`
 }
@@ -27,7 +27,7 @@ type ServicesPlatformSpec struct {
 // ServiceSpec defines the desired state of a platform service
 // +k8s:openapi-gen=true
 type ServiceSpec struct {
-       // Determines whether "prod" profile workflows should be configured to 
use this service
+       // Determines whether workflows without the `sonataflow.org/profile: 
dev` annotation should be configured to use this service
        // +optional
        Enabled *bool `json:"enabled,omitempty"`
        // Persists service to a datasource of choice. Ephemeral by default.
diff --git a/api/v1alpha08/sonataflowplatform_types.go 
b/api/v1alpha08/sonataflowplatform_types.go
index fd2ba013..0ee7e9ab 100644
--- a/api/v1alpha08/sonataflowplatform_types.go
+++ b/api/v1alpha08/sonataflowplatform_types.go
@@ -41,11 +41,11 @@ type SonataFlowPlatformSpec struct {
        // +optional
        // 
+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="DevMode"
        DevMode DevModePlatformSpec `json:"devMode,omitempty"`
-       // Services attributes for deploying supporting applications like Data 
Index.
-       // Only workflows with the proper annotation will be configured to use 
these service(s).
-       // `sonataflow.org/profile: prod`
+       // Services attributes for deploying supporting applications like Data 
Index & Job Service.
+       // Only workflows without the `sonataflow.org/profile: dev` annotation 
will be configured to use these service(s).
+       // Setting this will override the use of any cluster-scoped services 
that might be defined via `SonataFlowClusterPlatform`.
        // +optional
-       Services ServicesPlatformSpec `json:"services,omitempty"`
+       Services *ServicesPlatformSpec `json:"services,omitempty"`
 }
 
 // PlatformCluster is the kind of orchestration cluster the platform is 
installed into
@@ -79,6 +79,35 @@ type SonataFlowPlatformStatus struct {
        // Info generic information related to the build
        
//+operator-sdk:csv:customresourcedefinitions:type=status,displayName="info"
        Info map[string]string `json:"info,omitempty"`
+       // ClusterPlatformRef information related to the (optional) active 
SonataFlowClusterPlatform
+       ClusterPlatformRef *SonataFlowClusterPlatformRefStatus 
`json:"clusterPlatformRef,omitempty"`
+}
+
+// SonataFlowClusterPlatformRefStatus information related to the (optional) 
active SonataFlowClusterPlatform
+// +k8s:openapi-gen=true
+type SonataFlowClusterPlatformRefStatus struct {
+       // Name of the active SonataFlowClusterPlatform
+       Name string `json:"name,omitempty"`
+       // PlatformRef displays which SonataFlowPlatform has been referenced by 
the active SonataFlowClusterPlatform
+       PlatformRef SonataFlowPlatformRef `json:"platformRef,omitempty"`
+       // Services displays which cluster-wide services are being used by this 
SonataFlowPlatform
+       Services *PlatformServicesStatus `json:"services,omitempty"`
+}
+
+// PlatformServicesStatus displays which cluster-wide services are being used 
by a SonataFlowPlatform
+// +k8s:openapi-gen=true
+type PlatformServicesStatus struct {
+       // DataIndexRef displays information on the cluster-wide Data Index 
service
+       DataIndexRef *PlatformServiceRefStatus `json:"dataIndexRef,omitempty"`
+       // JobServiceRef displays information on the cluster-wide Job Service
+       JobServiceRef *PlatformServiceRefStatus `json:"jobServiceRef,omitempty"`
+}
+
+// PlatformServiceRefStatus displays information on a cluster-wide service
+// +k8s:openapi-gen=true
+type PlatformServiceRefStatus struct {
+       // Url displays the base url of a cluster-wide service
+       Url string `json:"url,omitempty"`
 }
 
 func (in *SonataFlowPlatformStatus) GetTopLevelConditionType() 
api.ConditionType {
diff --git a/api/v1alpha08/zz_generated.deepcopy.go 
b/api/v1alpha08/zz_generated.deepcopy.go
index 3c24b8c6..d4449494 100644
--- a/api/v1alpha08/zz_generated.deepcopy.go
+++ b/api/v1alpha08/zz_generated.deepcopy.go
@@ -360,6 +360,46 @@ 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 *PlatformServiceRefStatus) DeepCopyInto(out 
*PlatformServiceRefStatus) {
+       *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new PlatformServiceRefStatus.
+func (in *PlatformServiceRefStatus) DeepCopy() *PlatformServiceRefStatus {
+       if in == nil {
+               return nil
+       }
+       out := new(PlatformServiceRefStatus)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *PlatformServicesStatus) DeepCopyInto(out *PlatformServicesStatus) {
+       *out = *in
+       if in.DataIndexRef != nil {
+               in, out := &in.DataIndexRef, &out.DataIndexRef
+               *out = new(PlatformServiceRefStatus)
+               **out = **in
+       }
+       if in.JobServiceRef != nil {
+               in, out := &in.JobServiceRef, &out.JobServiceRef
+               *out = new(PlatformServiceRefStatus)
+               **out = **in
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new PlatformServicesStatus.
+func (in *PlatformServicesStatus) DeepCopy() *PlatformServicesStatus {
+       if in == nil {
+               return nil
+       }
+       out := new(PlatformServicesStatus)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *PodSpec) DeepCopyInto(out *PodSpec) {
        *out = *in
@@ -764,6 +804,118 @@ func (in *SonataFlowBuildStatus) DeepCopy() 
*SonataFlowBuildStatus {
        return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *SonataFlowClusterPlatform) DeepCopyInto(out 
*SonataFlowClusterPlatform) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+       out.Spec = in.Spec
+       in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SonataFlowClusterPlatform.
+func (in *SonataFlowClusterPlatform) DeepCopy() *SonataFlowClusterPlatform {
+       if in == nil {
+               return nil
+       }
+       out := new(SonataFlowClusterPlatform)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, 
creating a new runtime.Object.
+func (in *SonataFlowClusterPlatform) DeepCopyObject() runtime.Object {
+       if c := in.DeepCopy(); c != nil {
+               return c
+       }
+       return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *SonataFlowClusterPlatformList) DeepCopyInto(out 
*SonataFlowClusterPlatformList) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ListMeta.DeepCopyInto(&out.ListMeta)
+       if in.Items != nil {
+               in, out := &in.Items, &out.Items
+               *out = make([]SonataFlowClusterPlatform, len(*in))
+               for i := range *in {
+                       (*in)[i].DeepCopyInto(&(*out)[i])
+               }
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SonataFlowClusterPlatformList.
+func (in *SonataFlowClusterPlatformList) DeepCopy() 
*SonataFlowClusterPlatformList {
+       if in == nil {
+               return nil
+       }
+       out := new(SonataFlowClusterPlatformList)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, 
creating a new runtime.Object.
+func (in *SonataFlowClusterPlatformList) DeepCopyObject() runtime.Object {
+       if c := in.DeepCopy(); c != nil {
+               return c
+       }
+       return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *SonataFlowClusterPlatformRefStatus) DeepCopyInto(out 
*SonataFlowClusterPlatformRefStatus) {
+       *out = *in
+       out.PlatformRef = in.PlatformRef
+       if in.Services != nil {
+               in, out := &in.Services, &out.Services
+               *out = new(PlatformServicesStatus)
+               (*in).DeepCopyInto(*out)
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SonataFlowClusterPlatformRefStatus.
+func (in *SonataFlowClusterPlatformRefStatus) DeepCopy() 
*SonataFlowClusterPlatformRefStatus {
+       if in == nil {
+               return nil
+       }
+       out := new(SonataFlowClusterPlatformRefStatus)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *SonataFlowClusterPlatformSpec) DeepCopyInto(out 
*SonataFlowClusterPlatformSpec) {
+       *out = *in
+       out.PlatformRef = in.PlatformRef
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SonataFlowClusterPlatformSpec.
+func (in *SonataFlowClusterPlatformSpec) DeepCopy() 
*SonataFlowClusterPlatformSpec {
+       if in == nil {
+               return nil
+       }
+       out := new(SonataFlowClusterPlatformSpec)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *SonataFlowClusterPlatformStatus) DeepCopyInto(out 
*SonataFlowClusterPlatformStatus) {
+       *out = *in
+       in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SonataFlowClusterPlatformStatus.
+func (in *SonataFlowClusterPlatformStatus) DeepCopy() 
*SonataFlowClusterPlatformStatus {
+       if in == nil {
+               return nil
+       }
+       out := new(SonataFlowClusterPlatformStatus)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *SonataFlowList) DeepCopyInto(out *SonataFlowList) {
        *out = *in
@@ -855,12 +1007,31 @@ func (in *SonataFlowPlatformList) DeepCopyObject() 
runtime.Object {
        return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *SonataFlowPlatformRef) DeepCopyInto(out *SonataFlowPlatformRef) {
+       *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SonataFlowPlatformRef.
+func (in *SonataFlowPlatformRef) DeepCopy() *SonataFlowPlatformRef {
+       if in == nil {
+               return nil
+       }
+       out := new(SonataFlowPlatformRef)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *SonataFlowPlatformSpec) DeepCopyInto(out *SonataFlowPlatformSpec) {
        *out = *in
        in.Build.DeepCopyInto(&out.Build)
        out.DevMode = in.DevMode
-       in.Services.DeepCopyInto(&out.Services)
+       if in.Services != nil {
+               in, out := &in.Services, &out.Services
+               *out = new(ServicesPlatformSpec)
+               (*in).DeepCopyInto(*out)
+       }
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SonataFlowPlatformSpec.
@@ -884,6 +1055,11 @@ func (in *SonataFlowPlatformStatus) DeepCopyInto(out 
*SonataFlowPlatformStatus)
                        (*out)[key] = val
                }
        }
+       if in.ClusterPlatformRef != nil {
+               in, out := &in.ClusterPlatformRef, &out.ClusterPlatformRef
+               *out = new(SonataFlowClusterPlatformRefStatus)
+               (*in).DeepCopyInto(*out)
+       }
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SonataFlowPlatformStatus.
diff --git 
a/bundle/manifests/sonataflow-operator-sonataflowclusterplatform-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml
 
b/bundle/manifests/sonataflow-operator-sonataflowclusterplatform-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml
new file mode 100644
index 00000000..4b7af794
--- /dev/null
+++ 
b/bundle/manifests/sonataflow-operator-sonataflowclusterplatform-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml
@@ -0,0 +1,27 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  creationTimestamp: null
+  labels:
+    app.kubernetes.io/component: rbac
+    app.kubernetes.io/created-by: sonataflow-operator
+    app.kubernetes.io/instance: sonataflowclusterplatform-viewer-role
+    app.kubernetes.io/managed-by: kustomize
+    app.kubernetes.io/name: clusterrole
+    app.kubernetes.io/part-of: sonataflow-operator
+  name: sonataflow-operator-sonataflowclusterplatform-viewer-role
+rules:
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms/status
+  verbs:
+  - get
diff --git a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml 
b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml
index 94baa4f1..9dced8cd 100644
--- a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml
+++ b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml
@@ -86,6 +86,19 @@ metadata:
             "timeout": "360s"
           }
         },
+        {
+          "apiVersion": "sonataflow.org/v1alpha08",
+          "kind": "SonataFlowClusterPlatform",
+          "metadata": {
+            "name": "sonataflow-clusterplatform"
+          },
+          "spec": {
+            "platformRef": {
+              "name": "sonataflow-platform",
+              "namespace": "sonataflow-operator-system"
+            }
+          }
+        },
         {
           "apiVersion": "sonataflow.org/v1alpha08",
           "kind": "SonataFlowPlatform",
@@ -167,6 +180,23 @@ spec:
         displayName: InnerBuild
         path: innerBuild
       version: v1alpha08
+    - description: SonataFlowClusterPlatform is the Schema for the 
sonataflowclusterplatforms
+        API
+      displayName: Sonata Flow Cluster Platform
+      kind: SonataFlowClusterPlatform
+      name: sonataflowclusterplatforms.sonataflow.org
+      specDescriptors:
+      - description: Name of the SonataFlowPlatform
+        displayName: Platform_Name
+        path: platformRef.name
+      - description: Namespace of the SonataFlowPlatform
+        displayName: Platform_NS
+        path: platformRef.namespace
+      statusDescriptors:
+      - description: Version the operator version controlling this 
ClusterPlatform
+        displayName: version
+        path: version
+      version: v1alpha08
     - description: SonataFlowPlatform is the descriptor for the workflow 
platform
         infrastructure.
       displayName: Sonata Flow Platform
@@ -386,6 +416,32 @@ spec:
           - get
           - patch
           - update
+        - apiGroups:
+          - sonataflow.org
+          resources:
+          - sonataflowclusterplatforms
+          verbs:
+          - create
+          - delete
+          - get
+          - list
+          - patch
+          - update
+          - watch
+        - apiGroups:
+          - sonataflow.org
+          resources:
+          - sonataflowclusterplatforms/finalizers
+          verbs:
+          - update
+        - apiGroups:
+          - sonataflow.org
+          resources:
+          - sonataflowclusterplatforms/status
+          verbs:
+          - get
+          - patch
+          - update
         - apiGroups:
           - sonataflow.org
           resources:
diff --git a/bundle/manifests/sonataflow.org_sonataflowclusterplatforms.yaml 
b/bundle/manifests/sonataflow.org_sonataflowclusterplatforms.yaml
new file mode 100644
index 00000000..faa83264
--- /dev/null
+++ b/bundle/manifests/sonataflow.org_sonataflowclusterplatforms.yaml
@@ -0,0 +1,120 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.9.2
+  creationTimestamp: null
+  name: sonataflowclusterplatforms.sonataflow.org
+spec:
+  group: sonataflow.org
+  names:
+    kind: SonataFlowClusterPlatform
+    listKind: SonataFlowClusterPlatformList
+    plural: sonataflowclusterplatforms
+    singular: sonataflowclusterplatform
+  scope: Cluster
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .spec.platformRef.name
+      name: Platform_Name
+      type: string
+    - jsonPath: .spec.platformRef.namespace
+      name: Platform_NS
+      type: string
+    - jsonPath: .status.conditions[?(@.type=='Succeed')].status
+      name: Ready
+      type: string
+    - jsonPath: .status.conditions[?(@.type=='Succeed')].reason
+      name: Reason
+      type: string
+    name: v1alpha08
+    schema:
+      openAPIV3Schema:
+        description: SonataFlowClusterPlatform is the Schema for the 
sonataflowclusterplatforms
+          API
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this 
representation
+              of an object. Servers should convert recognized schemas to the 
latest
+              internal value, and may reject unrecognized values. More info: 
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST 
resource this
+              object represents. Servers may infer this from the endpoint the 
client
+              submits requests to. Cannot be updated. In CamelCase. More info: 
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: SonataFlowClusterPlatformSpec defines the desired 
state of
+              SonataFlowClusterPlatform
+            properties:
+              platformRef:
+                description: SonataFlowPlatformRef defines which existing 
SonataFlowPlatform's
+                  supporting services should be used cluster-wide.
+                properties:
+                  name:
+                    description: Name of the SonataFlowPlatform
+                    type: string
+                  namespace:
+                    description: Namespace of the SonataFlowPlatform
+                    type: string
+                required:
+                - name
+                - namespace
+                type: object
+            required:
+            - platformRef
+            type: object
+          status:
+            description: SonataFlowClusterPlatformStatus defines the observed 
state
+              of SonataFlowClusterPlatform
+            properties:
+              conditions:
+                description: The latest available observations of a resource's 
current
+                  state.
+                items:
+                  description: Condition describes the common structure for 
conditions
+                    in our types
+                  properties:
+                    lastUpdateTime:
+                      description: The last time this condition was updated.
+                      format: date-time
+                      type: string
+                    message:
+                      description: A human-readable message indicating details 
about
+                        the transition.
+                      type: string
+                    reason:
+                      description: The reason for the condition's last 
transition.
+                      type: string
+                    status:
+                      description: Status of the condition, one of True, 
False, Unknown.
+                      type: string
+                    type:
+                      description: Type condition for the given object
+                      type: string
+                  required:
+                  - status
+                  - type
+                  type: object
+                type: array
+              observedGeneration:
+                description: The generation observed by the deployment 
controller.
+                format: int64
+                type: integer
+              version:
+                description: Version the operator version controlling this 
ClusterPlatform
+                type: string
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: null
+  storedVersions: null
diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml 
b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml
index a8437bac..5910e8b7 100644
--- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml
+++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml
@@ -422,17 +422,18 @@ spec:
                 type: object
               services:
                 description: 'Services attributes for deploying supporting 
applications
-                  like Data Index. Only workflows with the proper annotation 
will
-                  be configured to use these service(s). 
`sonataflow.org/profile:
-                  prod`'
+                  like Data Index & Job Service. Only workflows without the 
`sonataflow.org/profile:
+                  dev` annotation will be configured to use these service(s). 
Setting
+                  this will override the use of any cluster-scoped services 
that might
+                  be defined via `SonataFlowClusterPlatform`.'
                 properties:
                   dataIndex:
-                    description: Deploys the Data Index service for use by 
"prod"
-                      profile workflows.
+                    description: 'Deploys the Data Index service for use by 
workflows
+                      without the `sonataflow.org/profile: dev` annotation.'
                     properties:
                       enabled:
-                        description: Determines whether "prod" profile 
workflows should
-                          be configured to use this service
+                        description: 'Determines whether workflows without the 
`sonataflow.org/profile:
+                          dev` annotation should be configured to use this 
service'
                         type: boolean
                       persistence:
                         description: Persists service to a datasource of 
choice. Ephemeral
@@ -8299,12 +8300,12 @@ spec:
                         type: object
                     type: object
                   jobService:
-                    description: Deploys the Job service for use by "prod" 
profile
-                      workflows.
+                    description: 'Deploys the Job service for use by workflows 
without
+                      the `sonataflow.org/profile: dev` annotation.'
                     properties:
                       enabled:
-                        description: Determines whether "prod" profile 
workflows should
-                          be configured to use this service
+                        description: 'Determines whether workflows without the 
`sonataflow.org/profile:
+                          dev` annotation should be configured to use this 
service'
                         type: boolean
                       persistence:
                         description: Persists service to a datasource of 
choice. Ephemeral
@@ -16182,6 +16183,51 @@ spec:
                 - kubernetes
                 - openshift
                 type: string
+              clusterPlatformRef:
+                description: ClusterPlatformRef information related to the 
(optional)
+                  active SonataFlowClusterPlatform
+                properties:
+                  name:
+                    description: Name of the active SonataFlowClusterPlatform
+                    type: string
+                  platformRef:
+                    description: PlatformRef displays which SonataFlowPlatform 
has
+                      been referenced by the active SonataFlowClusterPlatform
+                    properties:
+                      name:
+                        description: Name of the SonataFlowPlatform
+                        type: string
+                      namespace:
+                        description: Namespace of the SonataFlowPlatform
+                        type: string
+                    required:
+                    - name
+                    - namespace
+                    type: object
+                  services:
+                    description: Services displays which cluster-wide services 
are
+                      being used by this SonataFlowPlatform
+                    properties:
+                      dataIndexRef:
+                        description: DataIndexRef displays information on the 
cluster-wide
+                          Data Index service
+                        properties:
+                          url:
+                            description: Url displays the base url of a 
cluster-wide
+                              service
+                            type: string
+                        type: object
+                      jobServiceRef:
+                        description: JobServiceRef displays information on the 
cluster-wide
+                          Job Service
+                        properties:
+                          url:
+                            description: Url displays the base url of a 
cluster-wide
+                              service
+                            type: string
+                        type: object
+                    type: object
+                type: object
               conditions:
                 description: The latest available observations of a resource's 
current
                   state.
diff --git a/config/crd/bases/sonataflow.org_sonataflowclusterplatforms.yaml 
b/config/crd/bases/sonataflow.org_sonataflowclusterplatforms.yaml
new file mode 100644
index 00000000..a740ddea
--- /dev/null
+++ b/config/crd/bases/sonataflow.org_sonataflowclusterplatforms.yaml
@@ -0,0 +1,115 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.9.2
+  creationTimestamp: null
+  name: sonataflowclusterplatforms.sonataflow.org
+spec:
+  group: sonataflow.org
+  names:
+    kind: SonataFlowClusterPlatform
+    listKind: SonataFlowClusterPlatformList
+    plural: sonataflowclusterplatforms
+    singular: sonataflowclusterplatform
+  scope: Cluster
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .spec.platformRef.name
+      name: Platform_Name
+      type: string
+    - jsonPath: .spec.platformRef.namespace
+      name: Platform_NS
+      type: string
+    - jsonPath: .status.conditions[?(@.type=='Succeed')].status
+      name: Ready
+      type: string
+    - jsonPath: .status.conditions[?(@.type=='Succeed')].reason
+      name: Reason
+      type: string
+    name: v1alpha08
+    schema:
+      openAPIV3Schema:
+        description: SonataFlowClusterPlatform is the Schema for the 
sonataflowclusterplatforms
+          API
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this 
representation
+              of an object. Servers should convert recognized schemas to the 
latest
+              internal value, and may reject unrecognized values. More info: 
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST 
resource this
+              object represents. Servers may infer this from the endpoint the 
client
+              submits requests to. Cannot be updated. In CamelCase. More info: 
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: SonataFlowClusterPlatformSpec defines the desired 
state of
+              SonataFlowClusterPlatform
+            properties:
+              platformRef:
+                description: SonataFlowPlatformRef defines which existing 
SonataFlowPlatform's
+                  supporting services should be used cluster-wide.
+                properties:
+                  name:
+                    description: Name of the SonataFlowPlatform
+                    type: string
+                  namespace:
+                    description: Namespace of the SonataFlowPlatform
+                    type: string
+                required:
+                - name
+                - namespace
+                type: object
+            required:
+            - platformRef
+            type: object
+          status:
+            description: SonataFlowClusterPlatformStatus defines the observed 
state
+              of SonataFlowClusterPlatform
+            properties:
+              conditions:
+                description: The latest available observations of a resource's 
current
+                  state.
+                items:
+                  description: Condition describes the common structure for 
conditions
+                    in our types
+                  properties:
+                    lastUpdateTime:
+                      description: The last time this condition was updated.
+                      format: date-time
+                      type: string
+                    message:
+                      description: A human-readable message indicating details 
about
+                        the transition.
+                      type: string
+                    reason:
+                      description: The reason for the condition's last 
transition.
+                      type: string
+                    status:
+                      description: Status of the condition, one of True, 
False, Unknown.
+                      type: string
+                    type:
+                      description: Type condition for the given object
+                      type: string
+                  required:
+                  - status
+                  - type
+                  type: object
+                type: array
+              observedGeneration:
+                description: The generation observed by the deployment 
controller.
+                format: int64
+                type: integer
+              version:
+                description: Version the operator version controlling this 
ClusterPlatform
+                type: string
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
diff --git a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml 
b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml
index 6fd5a9b5..9db30353 100644
--- a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml
+++ b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml
@@ -423,17 +423,18 @@ spec:
                 type: object
               services:
                 description: 'Services attributes for deploying supporting 
applications
-                  like Data Index. Only workflows with the proper annotation 
will
-                  be configured to use these service(s). 
`sonataflow.org/profile:
-                  prod`'
+                  like Data Index & Job Service. Only workflows without the 
`sonataflow.org/profile:
+                  dev` annotation will be configured to use these service(s). 
Setting
+                  this will override the use of any cluster-scoped services 
that might
+                  be defined via `SonataFlowClusterPlatform`.'
                 properties:
                   dataIndex:
-                    description: Deploys the Data Index service for use by 
"prod"
-                      profile workflows.
+                    description: 'Deploys the Data Index service for use by 
workflows
+                      without the `sonataflow.org/profile: dev` annotation.'
                     properties:
                       enabled:
-                        description: Determines whether "prod" profile 
workflows should
-                          be configured to use this service
+                        description: 'Determines whether workflows without the 
`sonataflow.org/profile:
+                          dev` annotation should be configured to use this 
service'
                         type: boolean
                       persistence:
                         description: Persists service to a datasource of 
choice. Ephemeral
@@ -8300,12 +8301,12 @@ spec:
                         type: object
                     type: object
                   jobService:
-                    description: Deploys the Job service for use by "prod" 
profile
-                      workflows.
+                    description: 'Deploys the Job service for use by workflows 
without
+                      the `sonataflow.org/profile: dev` annotation.'
                     properties:
                       enabled:
-                        description: Determines whether "prod" profile 
workflows should
-                          be configured to use this service
+                        description: 'Determines whether workflows without the 
`sonataflow.org/profile:
+                          dev` annotation should be configured to use this 
service'
                         type: boolean
                       persistence:
                         description: Persists service to a datasource of 
choice. Ephemeral
@@ -16183,6 +16184,51 @@ spec:
                 - kubernetes
                 - openshift
                 type: string
+              clusterPlatformRef:
+                description: ClusterPlatformRef information related to the 
(optional)
+                  active SonataFlowClusterPlatform
+                properties:
+                  name:
+                    description: Name of the active SonataFlowClusterPlatform
+                    type: string
+                  platformRef:
+                    description: PlatformRef displays which SonataFlowPlatform 
has
+                      been referenced by the active SonataFlowClusterPlatform
+                    properties:
+                      name:
+                        description: Name of the SonataFlowPlatform
+                        type: string
+                      namespace:
+                        description: Namespace of the SonataFlowPlatform
+                        type: string
+                    required:
+                    - name
+                    - namespace
+                    type: object
+                  services:
+                    description: Services displays which cluster-wide services 
are
+                      being used by this SonataFlowPlatform
+                    properties:
+                      dataIndexRef:
+                        description: DataIndexRef displays information on the 
cluster-wide
+                          Data Index service
+                        properties:
+                          url:
+                            description: Url displays the base url of a 
cluster-wide
+                              service
+                            type: string
+                        type: object
+                      jobServiceRef:
+                        description: JobServiceRef displays information on the 
cluster-wide
+                          Job Service
+                        properties:
+                          url:
+                            description: Url displays the base url of a 
cluster-wide
+                              service
+                            type: string
+                        type: object
+                    type: object
+                type: object
               conditions:
                 description: The latest available observations of a resource's 
current
                   state.
diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml
index a07d24bb..49241231 100644
--- a/config/crd/kustomization.yaml
+++ b/config/crd/kustomization.yaml
@@ -5,6 +5,7 @@ resources:
 - bases/sonataflow.org_sonataflows.yaml
 - bases/sonataflow.org_sonataflowbuilds.yaml
 - bases/sonataflow.org_sonataflowplatforms.yaml
+- bases/sonataflow.org_sonataflowclusterplatforms.yaml
 #+kubebuilder:scaffold:crdkustomizeresource
 
 patchesStrategicMerge:
@@ -13,6 +14,7 @@ patchesStrategicMerge:
 #- patches/webhook_in_workflows.yaml
 #- patches/webhook_in_sonataflows.yaml
 #- patches/webhook_in_sonataflowplatforms.yaml
+#- patches/webhook_in_sonataflowclusterplatforms.yaml
 #+kubebuilder:scaffold:crdkustomizewebhookpatch
 
 # [CERTMANAGER] To enable cert-manager, uncomment all the sections with 
[CERTMANAGER] prefix.
@@ -20,6 +22,7 @@ patchesStrategicMerge:
 #- patches/cainjection_in_workflows.yaml
 #- patches/cainjection_in_sonataflowworkflows.yaml
 #- patches/cainjection_in_sonataflowplatforms.yaml
+#- patches/cainjection_in_sonataflowclusterplatforms.yaml
 #+kubebuilder:scaffold:crdkustomizecainjectionpatch
 
 # the following config is for teaching kustomize how to do kustomization for 
CRDs.
diff --git a/config/crd/patches/cainjection_in_sonataflowclusterplatforms.yaml 
b/config/crd/patches/cainjection_in_sonataflowclusterplatforms.yaml
new file mode 100644
index 00000000..dc0274cd
--- /dev/null
+++ b/config/crd/patches/cainjection_in_sonataflowclusterplatforms.yaml
@@ -0,0 +1,7 @@
+# The following patch adds a directive for certmanager to inject CA into the 
CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    cert-manager.io/inject-ca-from: 
$(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+  name: sonataflowclusterplatforms.sonataflow.org
diff --git a/config/crd/patches/webhook_in_sonataflowclusterplatforms.yaml 
b/config/crd/patches/webhook_in_sonataflowclusterplatforms.yaml
new file mode 100644
index 00000000..ed7b0f41
--- /dev/null
+++ b/config/crd/patches/webhook_in_sonataflowclusterplatforms.yaml
@@ -0,0 +1,16 @@
+# The following patch enables a conversion webhook for the CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: sonataflowclusterplatforms.sonataflow.org
+spec:
+  conversion:
+    strategy: Webhook
+    webhook:
+      clientConfig:
+        service:
+          namespace: system
+          name: webhook-service
+          path: /convert
+      conversionReviewVersions:
+      - v1
diff --git 
a/config/manifests/bases/sonataflow-operator.clusterserviceversion.yaml 
b/config/manifests/bases/sonataflow-operator.clusterserviceversion.yaml
index dc1b051e..fcd5460e 100644
--- a/config/manifests/bases/sonataflow-operator.clusterserviceversion.yaml
+++ b/config/manifests/bases/sonataflow-operator.clusterserviceversion.yaml
@@ -64,6 +64,23 @@ spec:
         displayName: InnerBuild
         path: innerBuild
       version: v1alpha08
+    - description: SonataFlowClusterPlatform is the Schema for the 
sonataflowclusterplatforms
+        API
+      displayName: Sonata Flow Cluster Platform
+      kind: SonataFlowClusterPlatform
+      name: sonataflowclusterplatforms.sonataflow.org
+      specDescriptors:
+      - description: Name of the SonataFlowPlatform
+        displayName: Platform_Name
+        path: platformRef.name
+      - description: Namespace of the SonataFlowPlatform
+        displayName: Platform_NS
+        path: platformRef.namespace
+      statusDescriptors:
+      - description: Version the operator version controlling this 
ClusterPlatform
+        displayName: version
+        path: version
+      version: v1alpha08
     - description: SonataFlowPlatform is the descriptor for the workflow 
platform
         infrastructure.
       displayName: Sonata Flow Platform
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index 49e76885..e09a00c2 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -31,6 +31,32 @@ rules:
   - get
   - patch
   - update
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms/finalizers
+  verbs:
+  - update
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms/status
+  verbs:
+  - get
+  - patch
+  - update
 - apiGroups:
   - sonataflow.org
   resources:
diff --git a/config/rbac/sonataflowclusterplatform_editor_role.yaml 
b/config/rbac/sonataflowclusterplatform_editor_role.yaml
new file mode 100644
index 00000000..26f5aa2c
--- /dev/null
+++ b/config/rbac/sonataflowclusterplatform_editor_role.yaml
@@ -0,0 +1,31 @@
+# permissions for end users to edit sonataflowclusterplatforms.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: clusterrole
+    app.kubernetes.io/instance: sonataflowclusterplatform-editor-role
+    app.kubernetes.io/component: rbac
+    app.kubernetes.io/created-by: sonataflow-operator
+    app.kubernetes.io/part-of: sonataflow-operator
+    app.kubernetes.io/managed-by: kustomize
+  name: sonataflowclusterplatform-editor-role
+rules:
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms/status
+  verbs:
+  - get
diff --git 
a/config/rbac/sonataflowclusterplatform_viewer_cluster_role_binding.yaml 
b/config/rbac/sonataflowclusterplatform_viewer_cluster_role_binding.yaml
new file mode 100644
index 00000000..d54f9428
--- /dev/null
+++ b/config/rbac/sonataflowclusterplatform_viewer_cluster_role_binding.yaml
@@ -0,0 +1,13 @@
+# allow users to view SonataFlowClusterPlatforms cluster-wide
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: sonataflowclusterplatform-viewer
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: sonataflowclusterplatform-viewer-role
+subjects:
+  - apiGroup: rbac.authorization.k8s.io
+    kind: Group
+    name: system:authenticated
\ No newline at end of file
diff --git a/config/rbac/sonataflowclusterplatform_viewer_role.yaml 
b/config/rbac/sonataflowclusterplatform_viewer_role.yaml
new file mode 100644
index 00000000..4a64a881
--- /dev/null
+++ b/config/rbac/sonataflowclusterplatform_viewer_role.yaml
@@ -0,0 +1,27 @@
+# permissions for end users to view sonataflowclusterplatforms.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: clusterrole
+    app.kubernetes.io/instance: sonataflowclusterplatform-viewer-role
+    app.kubernetes.io/component: rbac
+    app.kubernetes.io/created-by: sonataflow-operator
+    app.kubernetes.io/part-of: sonataflow-operator
+    app.kubernetes.io/managed-by: kustomize
+  name: sonataflowclusterplatform-viewer-role
+rules:
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms/status
+  verbs:
+  - get
diff --git a/config/samples/kustomization.yaml 
b/config/samples/kustomization.yaml
index d4ca147b..642faef1 100644
--- a/config/samples/kustomization.yaml
+++ b/config/samples/kustomization.yaml
@@ -3,4 +3,5 @@ resources:
 - sonataflow.org_v1alpha08_sonataflow.yaml
 - sonataflow.org_v1alpha08_sonataflowplatform.yaml
 - sonataflow.org_v1alpha08_sonataflowbuild.yaml
+- sonataflow.org_v1alpha08_sonataflowclusterplatform.yaml
 #+kubebuilder:scaffold:manifestskustomizesamples
diff --git 
a/config/samples/sonataflow.org_v1alpha08_sonataflowclusterplatform.yaml 
b/config/samples/sonataflow.org_v1alpha08_sonataflowclusterplatform.yaml
new file mode 100644
index 00000000..2117a7a0
--- /dev/null
+++ b/config/samples/sonataflow.org_v1alpha08_sonataflowclusterplatform.yaml
@@ -0,0 +1,8 @@
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlowClusterPlatform
+metadata:
+  name: sonataflow-clusterplatform
+spec:
+  platformRef:
+    name: sonataflow-platform
+    namespace: sonataflow-operator-system
diff --git a/controllers/clusterplatform/action.go 
b/controllers/clusterplatform/action.go
new file mode 100644
index 00000000..20ba9dcf
--- /dev/null
+++ b/controllers/clusterplatform/action.go
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 clusterplatform
+
+import (
+       "context"
+
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/container-builder/client"
+
+       v08 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+)
+
+// Action --.
+type Action interface {
+       client.Injectable
+
+       // a user friendly name for the action
+       Name() string
+
+       // returns true if the action can handle the cluster platform
+       CanHandle(ctx context.Context, cPlatform 
*v08.SonataFlowClusterPlatform) bool
+
+       // executes the handling function
+       Handle(ctx context.Context, cPlatform *v08.SonataFlowClusterPlatform) 
error
+}
+
+type baseAction struct {
+       client client.Client
+}
+
+func (action *baseAction) InjectClient(client client.Client) {
+       action.client = client
+}
diff --git a/controllers/clusterplatform/clusterplatform.go 
b/controllers/clusterplatform/clusterplatform.go
new file mode 100644
index 00000000..6c1ffe45
--- /dev/null
+++ b/controllers/clusterplatform/clusterplatform.go
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 clusterplatform
+
+import (
+       "context"
+
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/api/metadata"
+       operatorapi 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       "github.com/apache/incubator-kie-kogito-serverless-operator/log"
+       k8serrors "k8s.io/apimachinery/pkg/api/errors"
+       "k8s.io/klog/v2"
+       ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// GetActiveClusterPlatform returns the currently installed active cluster 
platform.
+func GetActiveClusterPlatform(ctx context.Context, c ctrl.Client) 
(*operatorapi.SonataFlowClusterPlatform, error) {
+       return getClusterPlatform(ctx, c, true)
+}
+
+// getClusterPlatform returns the currently active cluster platform or any 
cluster platform existing in the cluster.
+func getClusterPlatform(ctx context.Context, c ctrl.Client, active bool) 
(*operatorapi.SonataFlowClusterPlatform, error) {
+       klog.V(log.D).InfoS("Finding available cluster platforms")
+
+       lst, err := listPrimaryClusterPlatforms(ctx, c)
+       if err != nil {
+               return nil, err
+       }
+
+       for _, cPlatform := range lst.Items {
+               if IsActive(&cPlatform) {
+                       klog.V(log.D).InfoS("Found active cluster platform", 
"platform", cPlatform.Name)
+                       return &cPlatform, nil
+               }
+       }
+
+       if !active && len(lst.Items) > 0 {
+               // does not require the cluster platform to be active, just 
return one if present
+               res := lst.Items[0]
+               klog.V(log.D).InfoS("Found cluster platform", "platform", 
res.Name)
+               return &res, nil
+       }
+       klog.V(log.I).InfoS("No cluster platform found")
+       return nil, 
k8serrors.NewNotFound(operatorapi.Resource(operatorapi.SonataFlowClusterPlatformKind),
 "")
+}
+
+// listPrimaryClusterPlatforms returns all non-secondary cluster platforms 
installed (only one will be active).
+func listPrimaryClusterPlatforms(ctx context.Context, c ctrl.Reader) 
(*operatorapi.SonataFlowClusterPlatformList, error) {
+       lst, err := listAllClusterPlatforms(ctx, c)
+       if err != nil {
+               return nil, err
+       }
+
+       filtered := &operatorapi.SonataFlowClusterPlatformList{}
+       for i := range lst.Items {
+               cPl := lst.Items[i]
+               if !IsSecondary(&cPl) {
+                       filtered.Items = append(filtered.Items, cPl)
+               }
+       }
+       return filtered, nil
+}
+
+// allDuplicatedClusterPlatforms returns true if every cluster platform has a 
"Duplicated" status set
+func allDuplicatedClusterPlatforms(ctx context.Context, c ctrl.Reader) bool {
+       lst, err := listAllClusterPlatforms(ctx, c)
+       if err != nil {
+               return false
+       }
+
+       for i := range lst.Items {
+               if !lst.Items[i].Status.IsDuplicated() {
+                       return false
+               }
+       }
+
+       return true
+}
+
+// listAllClusterPlatforms returns all clusterplatforms installed.
+func listAllClusterPlatforms(ctx context.Context, c ctrl.Reader) 
(*operatorapi.SonataFlowClusterPlatformList, error) {
+       lst := operatorapi.NewSonataFlowClusterPlatformList()
+       if err := c.List(ctx, &lst); err != nil {
+               return nil, err
+       }
+       return &lst, nil
+}
+
+// IsActive determines if the given cluster platform is being used.
+func IsActive(p *operatorapi.SonataFlowClusterPlatform) bool {
+       return p.Status.IsReady() && !p.Status.IsDuplicated()
+}
+
+// IsSecondary determines if the given cluster platform is marked as secondary.
+func IsSecondary(p *operatorapi.SonataFlowClusterPlatform) bool {
+       if l, ok := p.Annotations[metadata.SecondaryPlatformAnnotation]; ok && 
l == "true" {
+               return true
+       }
+       return false
+}
diff --git a/controllers/clusterplatform/initialize.go 
b/controllers/clusterplatform/initialize.go
new file mode 100644
index 00000000..15a9b9f8
--- /dev/null
+++ b/controllers/clusterplatform/initialize.go
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 clusterplatform
+
+import (
+       "context"
+       "fmt"
+
+       "github.com/apache/incubator-kie-kogito-serverless-operator/api"
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/api/metadata"
+       operatorapi 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       "github.com/apache/incubator-kie-kogito-serverless-operator/log"
+       k8serrors "k8s.io/apimachinery/pkg/api/errors"
+       "k8s.io/apimachinery/pkg/types"
+       "k8s.io/klog/v2"
+)
+
+// NewInitializeAction returns an action that initializes the platform 
configuration when not provided by the user.
+func NewInitializeAction() Action {
+       return &initializeAction{}
+}
+
+type initializeAction struct {
+       baseAction
+}
+
+func (action *initializeAction) Name() string {
+       return "initialize"
+}
+
+func (action *initializeAction) CanHandle(ctx context.Context, cPlatform 
*operatorapi.SonataFlowClusterPlatform) bool {
+       return !cPlatform.Status.IsDuplicated() || 
allDuplicatedClusterPlatforms(ctx, action.client)
+}
+
+func (action *initializeAction) Handle(ctx context.Context, cPlatform 
*operatorapi.SonataFlowClusterPlatform) error {
+       duplicate, err := action.isPrimaryDuplicate(ctx, cPlatform)
+       if err != nil {
+               return err
+       }
+       if duplicate {
+               // another cluster platform already present
+               if !cPlatform.Status.IsDuplicated() {
+                       
cPlatform.Status.Manager().MarkFalse(api.SucceedConditionType, 
operatorapi.PlatformDuplicatedReason, "")
+               }
+               return nil
+       }
+       cPlatform.Status.Version = metadata.SpecVersion
+       platformRef := cPlatform.Spec.PlatformRef
+
+       // Check referenced platform status
+       platform := &operatorapi.SonataFlowPlatform{}
+       err = action.client.Get(ctx, types.NamespacedName{Namespace: 
platformRef.Namespace, Name: platformRef.Name}, platform)
+       if err != nil {
+               if k8serrors.IsNotFound(err) {
+                       klog.V(log.D).InfoS("%s platform does not exist in %s 
namespace.", platformRef.Name, platformRef.Namespace)
+                       
cPlatform.Status.Manager().MarkFalse(api.SucceedConditionType, 
operatorapi.PlatformNotFoundReason,
+                               fmt.Sprintf("%s platform does not exist in %s 
namespace.", platformRef.Name, platformRef.Namespace))
+                       return nil
+               }
+               return err
+       }
+
+       if platform != nil {
+               condition := platform.Status.GetTopLevelCondition()
+               if condition.IsTrue() {
+                       klog.V(log.D).InfoS("Referenced SonataFlowPlatform 
'%s/%s' is ready", platformRef.Namespace, platformRef.Name)
+                       
cPlatform.Status.Manager().MarkTrueWithReason(api.SucceedConditionType, "",
+                               "Referenced SonataFlowPlatform '%s/%s' is 
ready", platformRef.Namespace, platformRef.Name)
+               } else if condition.IsFalse() {
+                       klog.V(log.D).InfoS("Referenced SonataFlowPlatform 
'%s/%s' not ready", platformRef.Namespace, platformRef.Name)
+                       
cPlatform.Status.Manager().MarkFalse(api.SucceedConditionType, 
operatorapi.PlatformFailureReason,
+                               "Referenced SonataFlowPlatform '%s/%s' not 
ready", platformRef.Namespace, platformRef.Name)
+               } else {
+                       klog.V(log.D).InfoS("Waiting for referenced 
SonataFlowPlatform '%s/%s' to be ready", platformRef.Namespace, 
platformRef.Name)
+                       
cPlatform.Status.Manager().MarkUnknown(api.SucceedConditionType, 
operatorapi.PlatformWarmingReason,
+                               "Waiting for referenced SonataFlowPlatform 
'%s/%s' to be ready", platformRef.Namespace, platformRef.Name)
+               }
+       }
+
+       return nil
+}
+
+// Function to double-check if there is already an active cluster platform
+func (action *initializeAction) isPrimaryDuplicate(ctx context.Context, 
cPlatform *operatorapi.SonataFlowClusterPlatform) (bool, error) {
+       if IsSecondary(cPlatform) {
+               // Always reconcile secondary cluster platforms
+               return false, nil
+       }
+       platforms, err := listPrimaryClusterPlatforms(ctx, action.client)
+       if err != nil {
+               return false, err
+       }
+       for _, p := range platforms.Items {
+               p := p // pin
+               if p.Name != cPlatform.Name && IsActive(&p) {
+                       return true, nil
+               }
+       }
+
+       return false, nil
+}
diff --git a/controllers/platform/k8s.go b/controllers/platform/k8s.go
index 8360f1bc..5a853f6a 100644
--- a/controllers/platform/k8s.go
+++ b/controllers/platform/k8s.go
@@ -29,14 +29,13 @@ import (
        
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants"
        "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"
        
"github.com/apache/incubator-kie-kogito-serverless-operator/workflowproj"
+       appsv1 "k8s.io/api/apps/v1"
+       corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/klog/v2"
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
-
-       kubeutil 
"github.com/apache/incubator-kie-kogito-serverless-operator/utils/kubernetes"
-       appsv1 "k8s.io/api/apps/v1"
-       corev1 "k8s.io/api/core/v1"
 )
 
 // NewServiceAction returns an action that deploys the services.
@@ -62,32 +61,36 @@ func (action *serviceAction) Handle(ctx context.Context, 
platform *operatorapi.S
                return nil, err
        }
 
-       if platform.Spec.Services.DataIndex != nil {
-               if err := createServiceComponents(ctx, action.client, platform, 
services.NewDataIndexHandler(platform)); err != nil {
-                       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
+                       }
                }
-       }
 
-       if platform.Spec.Services.JobService != nil {
-               if err := createServiceComponents(ctx, action.client, platform, 
services.NewJobServiceHandler(platform)); 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
+                       }
                }
        }
 
        return platform, nil
 }
 
-func createServiceComponents(ctx context.Context, client client.Client, 
platform *operatorapi.SonataFlowPlatform, psh services.PlatformServiceHandler) 
error {
-       if err := createConfigMap(ctx, client, platform, psh); err != nil {
+func createOrUpdateServiceComponents(ctx context.Context, client 
client.Client, platform *operatorapi.SonataFlowPlatform, psh 
services.PlatformServiceHandler) error {
+       if err := createOrUpdateConfigMap(ctx, client, platform, psh); err != 
nil {
                return err
        }
-       if err := createDeployment(ctx, client, platform, psh); err != nil {
+       if err := createOrUpdateDeployment(ctx, client, platform, psh); err != 
nil {
                return err
        }
-       return createService(ctx, client, platform, psh)
+       return createOrUpdateService(ctx, client, platform, psh)
 }
 
-func createDeployment(ctx context.Context, client client.Client, platform 
*operatorapi.SonataFlowPlatform, psh services.PlatformServiceHandler) error {
+func createOrUpdateDeployment(ctx context.Context, client client.Client, 
platform *operatorapi.SonataFlowPlatform, psh services.PlatformServiceHandler) 
error {
        readyProbe := &corev1.Probe{
                ProbeHandler: corev1.ProbeHandler{
                        HTTPGet: &corev1.HTTPGetAction{
@@ -193,7 +196,7 @@ func createDeployment(ctx context.Context, client 
client.Client, platform *opera
        return nil
 }
 
-func createService(ctx context.Context, client client.Client, platform 
*operatorapi.SonataFlowPlatform, psh services.PlatformServiceHandler) error {
+func createOrUpdateService(ctx context.Context, client client.Client, platform 
*operatorapi.SonataFlowPlatform, psh services.PlatformServiceHandler) error {
        lbl, selectorLbl := getLabels(platform, psh)
        dataSvcSpec := corev1.ServiceSpec{
                Ports: []corev1.ServicePort{
@@ -241,7 +244,7 @@ func getLabels(platform *operatorapi.SonataFlowPlatform, 
psh services.PlatformSe
        return lbl, selectorLbl
 }
 
-func createConfigMap(ctx context.Context, client client.Client, platform 
*operatorapi.SonataFlowPlatform, psh services.PlatformServiceHandler) error {
+func createOrUpdateConfigMap(ctx context.Context, client client.Client, 
platform *operatorapi.SonataFlowPlatform, psh services.PlatformServiceHandler) 
error {
        handler, err := services.NewServiceAppPropertyHandler(psh)
        if err != nil {
                return err
diff --git a/controllers/platform/platformutils.go 
b/controllers/platform/platformutils.go
index 9249bed0..ae14c40e 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.go 
b/controllers/platform/services/properties.go
index 5c411f38..dbe6dc3f 100644
--- a/controllers/platform/services/properties.go
+++ b/controllers/platform/services/properties.go
@@ -160,7 +160,8 @@ func GenerateDataIndexWorkflowProperties(workflow 
*operatorapi.SonataFlow, platf
        props := properties.NewProperties()
        props.Set(constants.KogitoProcessDefinitionsEventsEnabled, "false")
        props.Set(constants.KogitoProcessInstancesEventsEnabled, "false")
-       if workflow != nil && !profiles.IsDevProfile(workflow) && 
dataIndexEnabled(platform) {
+       di := NewDataIndexHandler(platform)
+       if workflow != nil && !profiles.IsDevProfile(workflow) && 
di.IsServiceEnabled() {
                props.Set(constants.KogitoProcessDefinitionsEventsEnabled, 
"true")
                props.Set(constants.KogitoProcessInstancesEventsEnabled, "true")
                di := NewDataIndexHandler(platform)
@@ -183,8 +184,8 @@ func GenerateJobServiceWorkflowProperties(workflow 
*operatorapi.SonataFlow, plat
        props := properties.NewProperties()
        props.Set(constants.JobServiceRequestEventsConnector, 
constants.QuarkusHTTP)
        props.Set(constants.JobServiceRequestEventsURL, 
fmt.Sprintf("%s://localhost/v2/jobs/events", constants.JobServiceURLProtocol))
-       if workflow != nil && !profiles.IsDevProfile(workflow) && 
jobServiceEnabled(platform) {
-               js := NewJobServiceHandler(platform)
+       js := NewJobServiceHandler(platform)
+       if workflow != nil && !profiles.IsDevProfile(workflow) && 
js.IsServiceEnabled() {
                p, err := js.GenerateWorkflowProperties()
                if err != nil {
                        return nil, err
diff --git a/controllers/platform/services/properties_services_test.go 
b/controllers/platform/services/properties_services_test.go
index 90da8083..9cdae2e9 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,6 +231,9 @@ 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{}
                }
@@ -234,6 +249,9 @@ 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 c04b4eb3..e49679e5 100644
--- a/controllers/platform/services/services.go
+++ b/controllers/platform/services/services.go
@@ -72,6 +72,22 @@ type PlatformServiceHandler interface {
        GenerateWorkflowProperties() (*properties.Properties, error)
        // GenerateServiceProperties returns a property object that contains 
the application properties required by the service deployment
        GenerateServiceProperties() (*properties.Properties, error)
+
+       // IsServiceSetInSpec returns true if the service is set in the spec.
+       IsServiceSetInSpec() bool
+       // IsServiceEnabledInSpec returns true if the service is enabled in the 
spec.
+       IsServiceEnabledInSpec() bool
+       // GetLocalServiceBaseUrl returns the base url of the local service
+       GetLocalServiceBaseUrl() string
+       // GetServiceBaseUrl returns the base url of the service, based on 
whether using local or cluster-scoped service.
+       GetServiceBaseUrl() string
+       // GetServiceUrl returns the service url, based on whether using local 
or cluster-scoped service.
+       GetServiceUrl() string
+       // IsServiceEnabled returns true if the service is enabled in either 
the spec or the status.clusterPlatformRef.
+       IsServiceEnabled() bool
+       // SetServiceUrlInStatus sets the service url in status. if reconciled 
instance does not have service set in spec AND
+       // if cluster referenced platform has said service enabled, use the 
cluster platform's service
+       SetServiceUrlInStatus(clusterRefPlatform 
*operatorapi.SonataFlowPlatform)
 }
 
 type DataIndexHandler struct {
@@ -102,6 +118,56 @@ func (d DataIndexHandler) GetServiceName() string {
        return fmt.Sprintf("%s-%s", d.platform.Name, 
constants.DataIndexServiceName)
 }
 
+func (d DataIndexHandler) SetServiceUrlInStatus(clusterRefPlatform 
*operatorapi.SonataFlowPlatform) {
+       psDI := NewDataIndexHandler(clusterRefPlatform)
+       if !isServicesSet(d.platform) && psDI.IsServiceEnabledInSpec() {
+               if d.platform.Status.ClusterPlatformRef != nil {
+                       if d.platform.Status.ClusterPlatformRef.Services == nil 
{
+                               d.platform.Status.ClusterPlatformRef.Services = 
&operatorapi.PlatformServicesStatus{}
+                       }
+                       
d.platform.Status.ClusterPlatformRef.Services.DataIndexRef = 
&operatorapi.PlatformServiceRefStatus{
+                               Url: psDI.GetLocalServiceBaseUrl(),
+                       }
+               }
+       }
+}
+
+func (d DataIndexHandler) IsServiceSetInSpec() bool {
+       return isDataIndexSet(d.platform)
+}
+
+func (d DataIndexHandler) IsServiceEnabledInSpec() bool {
+       return isDataIndexEnabled(d.platform)
+}
+
+func (d DataIndexHandler) isServiceEnabledInStatus() bool {
+       return d.platform != nil && d.platform.Status.ClusterPlatformRef != nil 
&&
+               d.platform.Status.ClusterPlatformRef.Services != nil && 
d.platform.Status.ClusterPlatformRef.Services.DataIndexRef != nil &&
+               !isServicesSet(d.platform)
+}
+
+func (d DataIndexHandler) IsServiceEnabled() bool {
+       return d.IsServiceEnabledInSpec() || d.isServiceEnabledInStatus()
+}
+
+func (d DataIndexHandler) GetServiceUrl() string {
+       return d.GetServiceBaseUrl() + 
constants.KogitoProcessInstancesEventsPath
+}
+
+func (d DataIndexHandler) GetServiceBaseUrl() string {
+       if d.IsServiceEnabledInSpec() {
+               return d.GetLocalServiceBaseUrl()
+       }
+       if d.isServiceEnabledInStatus() {
+               return 
d.platform.Status.ClusterPlatformRef.Services.DataIndexRef.Url
+       }
+       return ""
+}
+
+func (d DataIndexHandler) GetLocalServiceBaseUrl() string {
+       return generateServiceURL(constants.KogitoServiceURLProtocol, 
d.platform.Namespace, d.GetServiceName())
+}
+
 func (d DataIndexHandler) GetEnvironmentVariables() []corev1.EnvVar {
        return []corev1.EnvVar{
                {
@@ -169,17 +235,16 @@ func (d DataIndexHandler) GetServiceCmName() string {
 
 func (d DataIndexHandler) GenerateWorkflowProperties() 
(*properties.Properties, error) {
        props := properties.NewProperties()
-       if d.platform.Spec.Services.DataIndex != nil {
-               dataIndexUrl := 
generateServiceURL(constants.KogitoProcessEventsProtocol, d.platform.Namespace, 
d.GetServiceName())
-               props.Set(constants.KogitoProcessDefinitionsEventsURL, 
fmt.Sprintf("%s/definitions", dataIndexUrl))
-               props.Set(constants.KogitoProcessInstancesEventsURL, 
fmt.Sprintf("%s/processes", dataIndexUrl))
+       if d.IsServiceEnabled() {
+               props.Set(constants.KogitoProcessDefinitionsEventsURL, 
d.GetServiceBaseUrl()+constants.KogitoProcessDefinitionsEventsPath)
+               props.Set(constants.KogitoProcessInstancesEventsURL, 
d.GetServiceUrl())
        }
        return props, nil
 }
 
 func (d DataIndexHandler) GenerateServiceProperties() (*properties.Properties, 
error) {
        props := properties.NewProperties()
-       props.Set(constants.KogitoServiceURLProperty, 
generateServiceURL(constants.KogitoServiceURLProtocol, d.platform.Namespace, 
d.GetServiceName()))
+       props.Set(constants.KogitoServiceURLProperty, 
d.GetLocalServiceBaseUrl())
        props.Set(constants.DataIndexKafkaSmallRyeHealthProperty, "false")
        return props, nil
 }
@@ -216,6 +281,56 @@ func (j JobServiceHandler) GetServiceCmName() string {
        return fmt.Sprintf("%s-props", j.GetServiceName())
 }
 
+func (j JobServiceHandler) SetServiceUrlInStatus(clusterRefPlatform 
*operatorapi.SonataFlowPlatform) {
+       psJS := NewJobServiceHandler(clusterRefPlatform)
+       if !isServicesSet(j.platform) && psJS.IsServiceEnabledInSpec() {
+               if j.platform.Status.ClusterPlatformRef != nil {
+                       if j.platform.Status.ClusterPlatformRef.Services == nil 
{
+                               j.platform.Status.ClusterPlatformRef.Services = 
&operatorapi.PlatformServicesStatus{}
+                       }
+                       
j.platform.Status.ClusterPlatformRef.Services.JobServiceRef = 
&operatorapi.PlatformServiceRefStatus{
+                               Url: psJS.GetLocalServiceBaseUrl(),
+                       }
+               }
+       }
+}
+
+func (j JobServiceHandler) IsServiceSetInSpec() bool {
+       return isJobServiceSet(j.platform)
+}
+
+func (j JobServiceHandler) IsServiceEnabledInSpec() bool {
+       return isJobServiceEnabled(j.platform)
+}
+
+func (j JobServiceHandler) isServiceEnabledInStatus() bool {
+       return j.platform != nil && j.platform.Status.ClusterPlatformRef != nil 
&&
+               j.platform.Status.ClusterPlatformRef.Services != nil && 
j.platform.Status.ClusterPlatformRef.Services.JobServiceRef != nil &&
+               !isServicesSet(j.platform)
+}
+
+func (j JobServiceHandler) IsServiceEnabled() bool {
+       return j.IsServiceEnabledInSpec() || j.isServiceEnabledInStatus()
+}
+
+func (j JobServiceHandler) GetServiceUrl() string {
+       return j.GetServiceBaseUrl() + constants.JobServiceURLPath
+}
+
+func (j JobServiceHandler) GetServiceBaseUrl() string {
+       if j.IsServiceEnabledInSpec() {
+               return j.GetLocalServiceBaseUrl()
+       }
+       if j.isServiceEnabledInStatus() {
+               return 
j.platform.Status.ClusterPlatformRef.Services.JobServiceRef.Url
+       }
+       return ""
+}
+
+func (j JobServiceHandler) GetLocalServiceBaseUrl() string {
+       return generateServiceURL(constants.JobServiceURLProtocol, 
j.platform.Namespace, j.GetServiceName())
+}
+
 func (j JobServiceHandler) GetEnvironmentVariables() []corev1.EnvVar {
        return []corev1.EnvVar{
                {
@@ -277,17 +392,17 @@ func (j JobServiceHandler) GenerateServiceProperties() 
(*properties.Properties,
        props.Set(constants.JobServiceKafkaSmallRyeHealthProperty, "false")
        // add data source reactive URL
        jspec := j.platform.Spec.Services.JobService
-       if jspec != nil && jspec.Persistence != nil && 
jspec.Persistence.PostgreSql != nil {
+       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
                }
                props.Set(constants.JobServiceDataSourceReactiveURL, 
dataSourceReactiveURL)
        }
-       if dataIndexEnabled(j.platform) {
+       if isDataIndexEnabled(j.platform) {
                di := NewDataIndexHandler(j.platform)
                props.Set(constants.JobServiceStatusChangeEvents, "true")
-               props.Set(constants.JobServiceStatusChangeEventsURL, 
fmt.Sprintf("%s/jobs", 
generateServiceURL(constants.KogitoProcessEventsProtocol, j.platform.Namespace, 
di.GetServiceName())))
+               props.Set(constants.JobServiceStatusChangeEventsURL, 
di.GetLocalServiceBaseUrl()+"/jobs")
        }
        props.Sort()
        return props, nil
@@ -295,17 +410,33 @@ func (j JobServiceHandler) GenerateServiceProperties() 
(*properties.Properties,
 
 func (j JobServiceHandler) GenerateWorkflowProperties() 
(*properties.Properties, error) {
        props := properties.NewProperties()
-       props.Set(constants.JobServiceRequestEventsURL, 
fmt.Sprintf("%s/v2/jobs/events", 
generateServiceURL(constants.KogitoProcessEventsProtocol, j.platform.Namespace, 
j.GetServiceName())))
+       if j.IsServiceEnabled() {
+               // add data source reactive URL
+               props.Set(constants.JobServiceRequestEventsURL, 
j.GetServiceUrl())
+       }
        return props, nil
 }
 
-func dataIndexEnabled(platform *operatorapi.SonataFlowPlatform) bool {
-       return platform != nil && platform.Spec.Services.DataIndex != nil &&
-               platform.Spec.Services.DataIndex.Enabled != nil && 
*platform.Spec.Services.DataIndex.Enabled
+func isDataIndexEnabled(platform *operatorapi.SonataFlowPlatform) bool {
+       return isDataIndexSet(platform) && 
platform.Spec.Services.DataIndex.Enabled != nil &&
+               *platform.Spec.Services.DataIndex.Enabled
+}
+
+func isJobServiceEnabled(platform *operatorapi.SonataFlowPlatform) bool {
+       return isJobServiceSet(platform) && 
platform.Spec.Services.JobService.Enabled != nil &&
+               *platform.Spec.Services.JobService.Enabled
+}
+
+func isDataIndexSet(platform *operatorapi.SonataFlowPlatform) bool {
+       return isServicesSet(platform) && platform.Spec.Services.DataIndex != 
nil
+}
+
+func isJobServiceSet(platform *operatorapi.SonataFlowPlatform) bool {
+       return isServicesSet(platform) && platform.Spec.Services.JobService != 
nil
 }
 
-func jobServiceEnabled(platform *operatorapi.SonataFlowPlatform) bool {
-       return platform != nil && platform.Spec.Services.JobService != nil && 
platform.Spec.Services.JobService.Enabled != nil && 
*platform.Spec.Services.JobService.Enabled
+func isServicesSet(platform *operatorapi.SonataFlowPlatform) bool {
+       return platform != nil && platform.Spec.Services != nil
 }
 
 func generateServiceURL(protocol string, namespace string, name string) string 
{
diff --git a/controllers/profiles/common/constants/platform_services.go 
b/controllers/profiles/common/constants/platform_services.go
index 99d29563..3db52e16 100644
--- a/controllers/profiles/common/constants/platform_services.go
+++ b/controllers/profiles/common/constants/platform_services.go
@@ -32,12 +32,15 @@ const (
        JobServiceStatusChangeEventsURL  = 
"mp.messaging.outgoing.kogito-job-service-job-status-events-http.url"
        JobServiceURLProtocol            = "http"
        JobServiceDataSourceReactiveURL  = "quarkus.datasource.reactive.url"
+       JobServiceURLPath                = "/v2/jobs/events"
 
        KogitoProcessEventsProtocol           = "http"
        KogitoProcessInstancesEventsURL       = 
"mp.messaging.outgoing.kogito-processinstances-events.url"
        KogitoProcessInstancesEventsEnabled   = 
"kogito.events.processinstances.enabled"
+       KogitoProcessInstancesEventsPath      = "/processes"
        KogitoProcessDefinitionsEventsURL     = 
"mp.messaging.outgoing.kogito-processdefinitions-events.url"
        KogitoProcessDefinitionsEventsEnabled = 
"kogito.events.processdefinitions.enabled"
+       KogitoProcessDefinitionsEventsPath    = "/definitions"
        KogitoUserTasksEventsEnabled          = 
"kogito.events.usertasks.enabled"
        KogitoEventsVariablesEnabled          = 
"kogito.events.variables.enabled"
        KogitoServiceURLProperty              = "kogito.service.url"
diff --git a/controllers/profiles/common/properties/application_test.go 
b/controllers/profiles/common/properties/application_test.go
index a4c5dcf3..2a95e967 100644
--- a/controllers/profiles/common/properties/application_test.go
+++ b/controllers/profiles/common/properties/application_test.go
@@ -200,7 +200,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,
                        },
@@ -604,6 +604,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{}
                }
@@ -613,6 +616,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{}
                }
@@ -634,6 +640,9 @@ 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{}
                }
diff --git a/controllers/sonataflowclusterplatform_controller.go 
b/controllers/sonataflowclusterplatform_controller.go
new file mode 100644
index 00000000..a511bbaf
--- /dev/null
+++ b/controllers/sonataflowclusterplatform_controller.go
@@ -0,0 +1,175 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 controllers
+
+import (
+       "context"
+       "fmt"
+       "time"
+
+       "github.com/apache/incubator-kie-kogito-serverless-operator/api"
+       operatorapi 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       clientr 
"github.com/apache/incubator-kie-kogito-serverless-operator/container-builder/client"
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/clusterplatform"
+       "github.com/apache/incubator-kie-kogito-serverless-operator/log"
+       corev1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/errors"
+       "k8s.io/apimachinery/pkg/runtime"
+       "k8s.io/apimachinery/pkg/types"
+       "k8s.io/client-go/rest"
+       "k8s.io/client-go/tools/record"
+       "k8s.io/klog/v2"
+       ctrlrun "sigs.k8s.io/controller-runtime"
+       "sigs.k8s.io/controller-runtime/pkg/client"
+       ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+       "sigs.k8s.io/controller-runtime/pkg/handler"
+       "sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+// SonataFlowClusterPlatformReconciler reconciles a SonataFlowClusterPlatform 
object
+type SonataFlowClusterPlatformReconciler struct {
+       // This Client, initialized using mgr.Client() above, is a split Client
+       // that reads objects from the cache and writes to the API server
+       ctrl.Client
+       // Non-caching Client
+       Reader   ctrl.Reader
+       Scheme   *runtime.Scheme
+       Config   *rest.Config
+       Recorder record.EventRecorder
+}
+
+//+kubebuilder:rbac:groups=sonataflow.org,resources=sonataflowclusterplatforms,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=sonataflow.org,resources=sonataflowclusterplatforms/status,verbs=get;update;patch
+//+kubebuilder:rbac:groups=sonataflow.org,resources=sonataflowclusterplatforms/finalizers,verbs=update
+
+// Reconcile is part of the main kubernetes reconciliation loop which aims to
+// move the current state of the cluster closer to the desired state.
+// TODO(user): Modify the Reconcile function to compare the state specified by
+// the SonataFlowClusterPlatform object against the actual cluster state, and 
then
+// perform operations to make the cluster state reflect the state specified by
+// the user.
+//
+// For more details, check Reconcile and its Result here:
+// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
+func (r *SonataFlowClusterPlatformReconciler) Reconcile(ctx context.Context, 
req reconcile.Request) (reconcile.Result, error) {
+
+       // Fetch the SonataFlowClusterPlatform instance
+       var instance operatorapi.SonataFlowClusterPlatform
+
+       err := r.Client.Get(ctx, req.NamespacedName, &instance)
+       if err != nil {
+               if errors.IsNotFound(err) {
+                       return reconcile.Result{}, nil
+               }
+               klog.V(log.E).ErrorS(err, "Failed to get 
SonataFlowClusterPlatform")
+               return reconcile.Result{}, err
+       }
+
+       instance.Status.Manager().InitializeConditions()
+
+       cli, _ := clientr.FromCtrlClientSchemeAndConfig(r.Client, r.Scheme, 
r.Config)
+       action := clusterplatform.NewInitializeAction()
+       action.InjectClient(cli)
+       klog.V(log.I).InfoS("Invoking action", "Name", action.Name())
+
+       target := instance.DeepCopy()
+
+       if action.CanHandle(ctx, target) {
+               if err = action.Handle(ctx, target); err != nil {
+                       
target.Status.Manager().MarkFalse(api.SucceedConditionType, 
operatorapi.PlatformFailureReason, err.Error())
+                       if err := r.Client.Status().Patch(ctx, target, 
ctrl.MergeFrom(&instance)); err != nil {
+                               return reconcile.Result{}, err
+                       }
+                       r.Recorder.Event(&instance, corev1.EventTypeWarning, 
"Failed", fmt.Sprintf("Failed to update SonataFlowClusterPlaform: %s", err))
+                       return reconcile.Result{}, err
+               }
+
+               if target != nil {
+                       target.Status.ObservedGeneration = instance.Generation
+
+                       if err := r.Client.Status().Patch(ctx, target, 
ctrl.MergeFrom(&instance)); err != nil {
+                               r.Recorder.Event(&instance, 
corev1.EventTypeNormal, "Status Updated", fmt.Sprintf("Updated cluster platform 
condition %s", instance.Status.GetTopLevelCondition()))
+                               return reconcile.Result{}, err
+                       }
+               }
+
+               // handle one action at time so the resource
+               // is always at its latest state
+               r.Recorder.Event(&instance, corev1.EventTypeNormal, "Updated", 
fmt.Sprintf("Updated cluster platform condition to  %s", 
instance.Status.GetTopLevelCondition()))
+
+               if target != nil && target.Status.IsReady() {
+                       return reconcile.Result{}, nil
+               }
+
+               // Requeue
+               return reconcile.Result{
+                       RequeueAfter: 5 * time.Second,
+               }, nil
+       }
+
+       return reconcile.Result{}, nil
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *SonataFlowClusterPlatformReconciler) SetupWithManager(mgr 
ctrlrun.Manager) error {
+       return ctrlrun.NewControllerManagedBy(mgr).
+               For(&operatorapi.SonataFlowClusterPlatform{}).
+               Watches(&operatorapi.SonataFlowPlatform{}, 
handler.EnqueueRequestsFromMapFunc(r.mapPlatformToClusterPlatformRequests)).
+               Watches(&operatorapi.SonataFlowClusterPlatform{}, 
handler.EnqueueRequestsFromMapFunc(r.mapClusterPlatformToClusterPlatformRequests)).
+               Complete(r)
+}
+
+// if actively referenced sonataflowplatform object is changed, reconcile the 
active SonataFlowClusterPlatform.
+func (r *SonataFlowClusterPlatformReconciler) 
mapPlatformToClusterPlatformRequests(ctx context.Context, object client.Object) 
[]reconcile.Request {
+       sfcPlatform, err := clusterplatform.GetActiveClusterPlatform(ctx, 
r.Client)
+       if err != nil && !errors.IsNotFound(err) {
+               klog.V(log.E).ErrorS(err, "Failed to get active 
SonataFlowClusterPlatform")
+               return nil
+       }
+
+       if sfcPlatform != nil {
+               sfpcRefNsName := types.NamespacedName{Namespace: 
sfcPlatform.Spec.PlatformRef.Namespace, Name: sfcPlatform.Spec.PlatformRef.Name}
+               if client.ObjectKeyFromObject(object) == sfpcRefNsName {
+                       return []reconcile.Request{{NamespacedName: 
client.ObjectKeyFromObject(sfcPlatform)}}
+               }
+       }
+       return nil
+}
+
+// if active sonataflowclusterplatform is changed, reconcile other 
SonataFlowClusterPlatforms.
+func (r *SonataFlowClusterPlatformReconciler) 
mapClusterPlatformToClusterPlatformRequests(ctx context.Context, object 
client.Object) []reconcile.Request {
+       sfcPlatform := object.(*operatorapi.SonataFlowClusterPlatform)
+       var requests []reconcile.Request
+       if sfcPlatform != nil && clusterplatform.IsActive(sfcPlatform) {
+               var scpList operatorapi.SonataFlowClusterPlatformList
+               if err := r.List(ctx, &scpList); err != nil {
+                       klog.V(log.E).ErrorS(err, "Could not list 
SonataFlowClusterPlatforms. "+
+                               "SonataFlowClusterPlatforms affected by changes 
to the active SonataFlowClusterPlatform %s will not be reconciled.",
+                               sfcPlatform.Name)
+                       return nil
+               }
+
+               scpNamespacedName := client.ObjectKeyFromObject(sfcPlatform)
+               for _, cPlatform := range scpList.Items {
+                       namespacedName := client.ObjectKeyFromObject(&cPlatform)
+                       // this check is required so that the active 
clusterplatform object doesn't  reconcile
+                       if scpNamespacedName != namespacedName {
+                               requests = append(requests, 
reconcile.Request{NamespacedName: namespacedName})
+                       }
+               }
+       }
+       return requests
+}
diff --git a/controllers/sonataflowplatform_controller.go 
b/controllers/sonataflowplatform_controller.go
index 595b8339..c0ccd399 100644
--- a/controllers/sonataflowplatform_controller.go
+++ b/controllers/sonataflowplatform_controller.go
@@ -24,26 +24,26 @@ import (
        "fmt"
        "time"
 
+       "github.com/apache/incubator-kie-kogito-serverless-operator/api"
+       operatorapi 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       clientr 
"github.com/apache/incubator-kie-kogito-serverless-operator/container-builder/client"
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/clusterplatform"
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/platform"
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/platform/services"
+       "github.com/apache/incubator-kie-kogito-serverless-operator/log"
        appsv1 "k8s.io/api/apps/v1"
        corev1 "k8s.io/api/core/v1"
        "k8s.io/apimachinery/pkg/api/errors"
        "k8s.io/apimachinery/pkg/runtime"
+       "k8s.io/apimachinery/pkg/types"
        "k8s.io/client-go/rest"
        "k8s.io/client-go/tools/record"
        "k8s.io/klog/v2"
-       "sigs.k8s.io/controller-runtime/pkg/reconcile"
-
-       "github.com/apache/incubator-kie-kogito-serverless-operator/api"
-
-       clientr 
"github.com/apache/incubator-kie-kogito-serverless-operator/container-builder/client"
-
-       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/platform"
-
        ctrlrun "sigs.k8s.io/controller-runtime"
+       "sigs.k8s.io/controller-runtime/pkg/client"
        ctrl "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/log"
+       "sigs.k8s.io/controller-runtime/pkg/handler"
+       "sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
 // SonataFlowPlatformReconciler reconciles a SonataFlowPlatform object
@@ -114,6 +114,10 @@ func (r *SonataFlowPlatformReconciler) Reconcile(ctx 
context.Context, req reconc
 
        target := instance.DeepCopy()
 
+       if err = r.SonataFlowPlatformUpdateStatus(ctx, req, target); err != nil 
{
+               return reconcile.Result{}, err
+       }
+
        for _, a := range actions {
                cli, _ := clientr.FromCtrlClientSchemeAndConfig(r.Client, 
r.Scheme, r.Config)
                a.InjectClient(cli)
@@ -166,6 +170,51 @@ func (r *SonataFlowPlatformReconciler) Reconcile(ctx 
context.Context, req reconc
 
 }
 
+// If an active cluster platform exists, update platform.Status accordingly
+func (r *SonataFlowPlatformReconciler) SonataFlowPlatformUpdateStatus(ctx 
context.Context, req reconcile.Request, target *operatorapi.SonataFlowPlatform) 
error {
+       // Fetch the active SonataFlowClusterPlatform instance
+       sfcPlatform, err := clusterplatform.GetActiveClusterPlatform(ctx, 
r.Client)
+       if err != nil && !errors.IsNotFound(err) {
+               klog.V(log.E).ErrorS(err, "Failed to get active 
SonataFlowClusterPlatform")
+               return err
+       }
+
+       if sfcPlatform != nil {
+               sfPlatform := &operatorapi.SonataFlowPlatform{}
+
+               platformRef := sfcPlatform.Spec.PlatformRef
+               namespacedName := types.NamespacedName{Namespace: 
platformRef.Namespace, Name: platformRef.Name}
+               if req.NamespacedName == namespacedName {
+                       sfPlatform = target.DeepCopy()
+               } else {
+                       // retrieve referenced platform object
+                       err := r.Reader.Get(ctx, namespacedName, sfPlatform)
+                       if err != nil && !errors.IsNotFound(err) {
+                               klog.V(log.E).ErrorS(err, "Failed to get 
referenced SonataFlowPlatform", namespacedName)
+                               return err
+                       }
+               }
+
+               target.Status.ClusterPlatformRef = 
&operatorapi.SonataFlowClusterPlatformRefStatus{
+                       Name: sfcPlatform.Name,
+                       PlatformRef: operatorapi.SonataFlowPlatformRef{
+                               Name:      platformRef.Name,
+                               Namespace: platformRef.Namespace,
+                       },
+               }
+
+               tpsDI := services.NewDataIndexHandler(target)
+               tpsDI.SetServiceUrlInStatus(sfPlatform)
+
+               tpsJS := services.NewJobServiceHandler(target)
+               tpsJS.SetServiceUrlInStatus(sfPlatform)
+       } else {
+               target.Status.ClusterPlatformRef = nil
+       }
+
+       return nil
+}
+
 // SetupWithManager sets up the controller with the Manager.
 func (r *SonataFlowPlatformReconciler) SetupWithManager(mgr ctrlrun.Manager) 
error {
        return ctrlrun.NewControllerManagedBy(mgr).
@@ -173,5 +222,54 @@ func (r *SonataFlowPlatformReconciler) 
SetupWithManager(mgr ctrlrun.Manager) err
                Owns(&appsv1.Deployment{}).
                Owns(&corev1.Service{}).
                Owns(&corev1.ConfigMap{}).
+               Watches(&operatorapi.SonataFlowPlatform{}, 
handler.EnqueueRequestsFromMapFunc(r.mapPlatformToPlatformRequests)).
+               Watches(&operatorapi.SonataFlowClusterPlatform{}, 
handler.EnqueueRequestsFromMapFunc(r.mapClusterPlatformToPlatformRequests)).
                Complete(r)
 }
+
+// if active clusterplatform object is changed, reconcile all 
SonataFlowPlatforms in the cluster.
+func (r *SonataFlowPlatformReconciler) 
mapClusterPlatformToPlatformRequests(ctx context.Context, object client.Object) 
[]reconcile.Request {
+       sfcPlatform := object.(*operatorapi.SonataFlowClusterPlatform)
+       if sfcPlatform != nil && clusterplatform.IsActive(sfcPlatform) {
+               return r.platformRequests(ctx, sfcPlatform, true)
+       }
+       return nil
+}
+
+// if actively referenced sonataflowplatform is changed, reconcile other 
SonataFlowPlatforms in the cluster.
+func (r *SonataFlowPlatformReconciler) mapPlatformToPlatformRequests(ctx 
context.Context, object client.Object) []reconcile.Request {
+       platform := object.(*operatorapi.SonataFlowPlatform)
+       sfcPlatform, err := clusterplatform.GetActiveClusterPlatform(ctx, 
r.Client)
+       if err != nil && !errors.IsNotFound(err) {
+               klog.V(log.E).ErrorS(err, "Failed to get active 
SonataFlowClusterPlatform")
+               return nil
+       }
+
+       if sfcPlatform != nil {
+               sfpcRefNsName := types.NamespacedName{Namespace: 
sfcPlatform.Spec.PlatformRef.Namespace, Name: sfcPlatform.Spec.PlatformRef.Name}
+               if client.ObjectKeyFromObject(platform) == sfpcRefNsName {
+                       return r.platformRequests(ctx, sfcPlatform, false)
+               }
+       }
+       return nil
+}
+
+func (r *SonataFlowPlatformReconciler) platformRequests(ctx context.Context, 
sfcPlatform *operatorapi.SonataFlowClusterPlatform, allPlatforms bool) 
[]reconcile.Request {
+       var plList operatorapi.SonataFlowPlatformList
+       if err := r.List(ctx, &plList, client.InNamespace("")); err != nil {
+               klog.V(log.E).ErrorS(err, "could not list SonataFlowPlatforms. 
"+
+                       "SonataFlowPlatforms affected by changes to the active 
SonataFlowPlatform or SonataFlowClusterPlatform object will not be reconciled.")
+               return nil
+       }
+
+       sfpcRefNsName := types.NamespacedName{Namespace: 
sfcPlatform.Spec.PlatformRef.Namespace, Name: sfcPlatform.Spec.PlatformRef.Name}
+       var requests []reconcile.Request
+       for _, platform := range plList.Items {
+               sfpNsName := client.ObjectKeyFromObject(&platform)
+               // this check is required so that the cluster-referenced 
platform object doesn't infinitely reconcile
+               if sfpNsName != sfpcRefNsName || allPlatforms {
+                       requests = append(requests, 
reconcile.Request{NamespacedName: sfpNsName})
+               }
+       }
+       return requests
+}
diff --git a/controllers/sonataflowplatform_controller_test.go 
b/controllers/sonataflowplatform_controller_test.go
index db0ba080..5594d644 100644
--- a/controllers/sonataflowplatform_controller_test.go
+++ b/controllers/sonataflowplatform_controller_test.go
@@ -23,6 +23,10 @@ import (
        "context"
        "testing"
 
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/platform/services"
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants"
+       "github.com/apache/incubator-kie-kogito-serverless-operator/test"
        "github.com/stretchr/testify/assert"
        appsv1 "k8s.io/api/apps/v1"
        corev1 "k8s.io/api/core/v1"
@@ -30,12 +34,6 @@ import (
        "k8s.io/client-go/rest"
        "k8s.io/client-go/tools/record"
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
-
-       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/platform/services"
-       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/profiles/common/constants"
-       "github.com/apache/incubator-kie-kogito-serverless-operator/test"
-
-       
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
 )
 
 var (
@@ -79,7 +77,7 @@ 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.DataIndex)
+               assert.Nil(t, ksp.Spec.Services)
                assert.Equal(t, v1alpha08.PlatformClusterKubernetes, 
ksp.Status.Cluster)
 
                assert.Equal(t, v1alpha08.PlatformCreatingReason, 
ksp.Status.GetTopLevelCondition().Reason)
@@ -89,7 +87,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{},
                }
 
@@ -168,7 +166,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,
@@ -257,7 +255,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{},
                }
 
@@ -334,7 +332,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,
@@ -415,7 +413,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{},
                }
@@ -475,4 +473,125 @@ func TestSonataFlowPlatformController(t *testing.T) {
                assert.NotContains(t, dep.Spec.Template.Spec.Containers[0].Env, 
envDataIndex)
 
        })
+       t.Run("verify that a basic reconcile of a cluster platform is performed 
without error", func(t *testing.T) {
+               namespace := t.Name()
+
+               // Create a SonataFlowClusterPlatform object with metadata and 
spec.
+               kscp := test.GetBaseClusterPlatformInReadyPhase(namespace)
+
+               // Create a SonataFlowPlatform object with metadata and spec.
+               ksp := test.GetBasePlatformInReadyPhase(namespace)
+               ksp.Spec.Services = &v1alpha08.ServicesPlatformSpec{
+                       DataIndex:  &v1alpha08.ServiceSpec{},
+                       JobService: &v1alpha08.ServiceSpec{},
+               }
+               ksp2 := test.GetBasePlatformInReadyPhase(namespace)
+               ksp2.Name = "ksp2"
+
+               // Create a fake client to mock API calls.
+               cl := 
test.NewSonataFlowClientBuilder().WithRuntimeObjects(kscp, ksp, 
ksp2).WithStatusSubresource(kscp, ksp, ksp2).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)
+               }
+
+               assert.NoError(t, cl.Get(context.TODO(), 
types.NamespacedName{Name: ksp.Name, Namespace: ksp.Namespace}, ksp))
+               assert.Greater(t, len(ksp2.Status.Conditions), 0)
+               assert.Nil(t, ksp2.Status.ClusterPlatformRef)
+
+               // Create a SonataFlowClusterPlatformReconciler object with the 
scheme and fake client.
+               cr := &SonataFlowClusterPlatformReconciler{cl, cl, cl.Scheme(), 
&rest.Config{}, &record.FakeRecorder{}}
+
+               // Mock request to simulate Reconcile() being called on an 
event for a
+               // watched resource .
+               cReq := reconcile.Request{
+                       NamespacedName: types.NamespacedName{
+                               Name: kscp.Name,
+                       },
+               }
+               _, err = cr.Reconcile(context.TODO(), cReq)
+               if err != nil {
+                       t.Fatalf("reconcile: (%v)", err)
+               }
+
+               assert.NoError(t, cl.Get(context.TODO(), 
types.NamespacedName{Name: kscp.Name}, kscp))
+               assert.NoError(t, cl.Get(context.TODO(), 
types.NamespacedName{Name: ksp.Name, Namespace: ksp.Namespace}, ksp))
+
+               // Perform some checks on the created CR
+               assert.True(t, ksp.Status.IsReady())
+               assert.True(t, kscp.Status.IsReady())
+               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.NotNil(t, ksp.Spec.Services.DataIndex)
+               assert.NotNil(t, ksp.Spec.Services.DataIndex.Enabled)
+               assert.Equal(t, true, *ksp.Spec.Services.DataIndex.Enabled)
+               assert.Equal(t, v1alpha08.PlatformClusterKubernetes, 
ksp.Status.Cluster)
+               assert.Equal(t, "", ksp.Status.GetTopLevelCondition().Reason)
+               assert.Equal(t, kscp.Name, ksp.Status.ClusterPlatformRef.Name)
+               assert.Equal(t, kscp.Spec.PlatformRef.Name, 
ksp.Status.ClusterPlatformRef.PlatformRef.Name)
+               assert.Equal(t, kscp.Spec.PlatformRef.Namespace, 
ksp.Status.ClusterPlatformRef.PlatformRef.Namespace)
+
+               assert.NotNil(t, ksp.Status.ClusterPlatformRef)
+               assert.Nil(t, ksp.Status.ClusterPlatformRef.Services)
+
+               req2 := reconcile.Request{
+                       NamespacedName: types.NamespacedName{
+                               Name:      ksp2.Name,
+                               Namespace: ksp2.Namespace,
+                       },
+               }
+               _, err = r.Reconcile(context.TODO(), req2)
+               if err != nil {
+                       t.Fatalf("reconcile: (%v)", err)
+               }
+
+               assert.NoError(t, cl.Get(context.TODO(), 
types.NamespacedName{Name: ksp2.Name, Namespace: ksp2.Namespace}, ksp2))
+               assert.True(t, ksp2.Status.IsReady())
+               assert.NotNil(t, ksp2.Status.ClusterPlatformRef)
+               assert.Equal(t, kscp.Name, ksp2.Status.ClusterPlatformRef.Name)
+               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.NotNil(t, ksp2.Status.ClusterPlatformRef.Services)
+               assert.NotNil(t, 
ksp2.Status.ClusterPlatformRef.Services.DataIndexRef)
+               assert.NotEmpty(t, 
ksp2.Status.ClusterPlatformRef.Services.DataIndexRef.Url)
+               assert.NotNil(t, 
ksp2.Status.ClusterPlatformRef.Services.JobServiceRef)
+               assert.NotEmpty(t, 
ksp2.Status.ClusterPlatformRef.Services.JobServiceRef.Url)
+
+               psDi := services.NewDataIndexHandler(ksp)
+               psDi2 := services.NewDataIndexHandler(ksp2)
+               assert.Equal(t, 
ksp2.Status.ClusterPlatformRef.Services.DataIndexRef.Url, 
psDi.GetLocalServiceBaseUrl())
+               assert.Equal(t, 
psDi.GetLocalServiceBaseUrl()+constants.KogitoProcessInstancesEventsPath, 
psDi2.GetServiceUrl())
+               psJs := services.NewJobServiceHandler(ksp)
+               psJs2 := services.NewJobServiceHandler(ksp2)
+               assert.Equal(t, 
ksp2.Status.ClusterPlatformRef.Services.JobServiceRef.Url, 
psJs.GetLocalServiceBaseUrl())
+               assert.Equal(t, 
psJs.GetLocalServiceBaseUrl()+constants.JobServiceURLPath, 
psJs2.GetServiceUrl())
+
+               ksp2.Spec.Services = &v1alpha08.ServicesPlatformSpec{}
+
+               assert.NoError(t, cl.Update(context.TODO(), ksp2))
+               _, err = r.Reconcile(context.TODO(), req2)
+               if err != nil {
+                       t.Fatalf("reconcile: (%v)", err)
+               }
+
+               assert.NoError(t, cl.Get(context.TODO(), 
types.NamespacedName{Name: ksp2.Name, Namespace: ksp2.Namespace}, ksp2))
+               assert.True(t, ksp2.Status.IsReady())
+               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/main.go b/main.go
index 645cfb88..a1040f7b 100644
--- a/main.go
+++ b/main.go
@@ -115,6 +115,16 @@ func main() {
                klog.V(log.E).ErrorS(err, "unable to create controller", 
"controller", "SonataFlowPlatform")
                os.Exit(1)
        }
+       if err = (&controllers.SonataFlowClusterPlatformReconciler{
+               Client:   mgr.GetClient(),
+               Scheme:   mgr.GetScheme(),
+               Reader:   mgr.GetAPIReader(),
+               Config:   mgr.GetConfig(),
+               Recorder: 
mgr.GetEventRecorderFor("cluster-platform-controller"),
+       }).SetupWithManager(mgr); err != nil {
+               klog.V(log.E).ErrorS(err, "unable to create controller", 
"controller", "SonataFlowClusterPlatform")
+               os.Exit(1)
+       }
        //+kubebuilder:scaffold:builder
 
        if utils.IsOpenShift() {
diff --git a/operator.yaml b/operator.yaml
index 973b5cac..a016cf25 100644
--- a/operator.yaml
+++ b/operator.yaml
@@ -364,6 +364,121 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.9.2
+  creationTimestamp: null
+  name: sonataflowclusterplatforms.sonataflow.org
+spec:
+  group: sonataflow.org
+  names:
+    kind: SonataFlowClusterPlatform
+    listKind: SonataFlowClusterPlatformList
+    plural: sonataflowclusterplatforms
+    singular: sonataflowclusterplatform
+  scope: Cluster
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .spec.platformRef.name
+      name: Platform_Name
+      type: string
+    - jsonPath: .spec.platformRef.namespace
+      name: Platform_NS
+      type: string
+    - jsonPath: .status.conditions[?(@.type=='Succeed')].status
+      name: Ready
+      type: string
+    - jsonPath: .status.conditions[?(@.type=='Succeed')].reason
+      name: Reason
+      type: string
+    name: v1alpha08
+    schema:
+      openAPIV3Schema:
+        description: SonataFlowClusterPlatform is the Schema for the 
sonataflowclusterplatforms
+          API
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this 
representation
+              of an object. Servers should convert recognized schemas to the 
latest
+              internal value, and may reject unrecognized values. More info: 
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST 
resource this
+              object represents. Servers may infer this from the endpoint the 
client
+              submits requests to. Cannot be updated. In CamelCase. More info: 
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: SonataFlowClusterPlatformSpec defines the desired 
state of
+              SonataFlowClusterPlatform
+            properties:
+              platformRef:
+                description: SonataFlowPlatformRef defines which existing 
SonataFlowPlatform's
+                  supporting services should be used cluster-wide.
+                properties:
+                  name:
+                    description: Name of the SonataFlowPlatform
+                    type: string
+                  namespace:
+                    description: Namespace of the SonataFlowPlatform
+                    type: string
+                required:
+                - name
+                - namespace
+                type: object
+            required:
+            - platformRef
+            type: object
+          status:
+            description: SonataFlowClusterPlatformStatus defines the observed 
state
+              of SonataFlowClusterPlatform
+            properties:
+              conditions:
+                description: The latest available observations of a resource's 
current
+                  state.
+                items:
+                  description: Condition describes the common structure for 
conditions
+                    in our types
+                  properties:
+                    lastUpdateTime:
+                      description: The last time this condition was updated.
+                      format: date-time
+                      type: string
+                    message:
+                      description: A human-readable message indicating details 
about
+                        the transition.
+                      type: string
+                    reason:
+                      description: The reason for the condition's last 
transition.
+                      type: string
+                    status:
+                      description: Status of the condition, one of True, 
False, Unknown.
+                      type: string
+                    type:
+                      description: Type condition for the given object
+                      type: string
+                  required:
+                  - status
+                  - type
+                  type: object
+                type: array
+              observedGeneration:
+                description: The generation observed by the deployment 
controller.
+                format: int64
+                type: integer
+              version:
+                description: Version the operator version controlling this 
ClusterPlatform
+                type: string
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.9.2
@@ -786,17 +901,18 @@ spec:
                 type: object
               services:
                 description: 'Services attributes for deploying supporting 
applications
-                  like Data Index. Only workflows with the proper annotation 
will
-                  be configured to use these service(s). 
`sonataflow.org/profile:
-                  prod`'
+                  like Data Index & Job Service. Only workflows without the 
`sonataflow.org/profile:
+                  dev` annotation will be configured to use these service(s). 
Setting
+                  this will override the use of any cluster-scoped services 
that might
+                  be defined via `SonataFlowClusterPlatform`.'
                 properties:
                   dataIndex:
-                    description: Deploys the Data Index service for use by 
"prod"
-                      profile workflows.
+                    description: 'Deploys the Data Index service for use by 
workflows
+                      without the `sonataflow.org/profile: dev` annotation.'
                     properties:
                       enabled:
-                        description: Determines whether "prod" profile 
workflows should
-                          be configured to use this service
+                        description: 'Determines whether workflows without the 
`sonataflow.org/profile:
+                          dev` annotation should be configured to use this 
service'
                         type: boolean
                       persistence:
                         description: Persists service to a datasource of 
choice. Ephemeral
@@ -8663,12 +8779,12 @@ spec:
                         type: object
                     type: object
                   jobService:
-                    description: Deploys the Job service for use by "prod" 
profile
-                      workflows.
+                    description: 'Deploys the Job service for use by workflows 
without
+                      the `sonataflow.org/profile: dev` annotation.'
                     properties:
                       enabled:
-                        description: Determines whether "prod" profile 
workflows should
-                          be configured to use this service
+                        description: 'Determines whether workflows without the 
`sonataflow.org/profile:
+                          dev` annotation should be configured to use this 
service'
                         type: boolean
                       persistence:
                         description: Persists service to a datasource of 
choice. Ephemeral
@@ -16546,6 +16662,51 @@ spec:
                 - kubernetes
                 - openshift
                 type: string
+              clusterPlatformRef:
+                description: ClusterPlatformRef information related to the 
(optional)
+                  active SonataFlowClusterPlatform
+                properties:
+                  name:
+                    description: Name of the active SonataFlowClusterPlatform
+                    type: string
+                  platformRef:
+                    description: PlatformRef displays which SonataFlowPlatform 
has
+                      been referenced by the active SonataFlowClusterPlatform
+                    properties:
+                      name:
+                        description: Name of the SonataFlowPlatform
+                        type: string
+                      namespace:
+                        description: Namespace of the SonataFlowPlatform
+                        type: string
+                    required:
+                    - name
+                    - namespace
+                    type: object
+                  services:
+                    description: Services displays which cluster-wide services 
are
+                      being used by this SonataFlowPlatform
+                    properties:
+                      dataIndexRef:
+                        description: DataIndexRef displays information on the 
cluster-wide
+                          Data Index service
+                        properties:
+                          url:
+                            description: Url displays the base url of a 
cluster-wide
+                              service
+                            type: string
+                        type: object
+                      jobServiceRef:
+                        description: JobServiceRef displays information on the 
cluster-wide
+                          Job Service
+                        properties:
+                          url:
+                            description: Url displays the base url of a 
cluster-wide
+                              service
+                            type: string
+                        type: object
+                    type: object
+                type: object
               conditions:
                 description: The latest available observations of a resource's 
current
                   state.
@@ -26175,6 +26336,32 @@ rules:
   - get
   - patch
   - update
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms/finalizers
+  verbs:
+  - update
+- apiGroups:
+  - sonataflow.org
+  resources:
+  - sonataflowclusterplatforms/status
+  verbs:
+  - get
+  - patch
+  - update
 - apiGroups:
   - sonataflow.org
   resources:
diff --git 
a/test/testdata/sonataflow.org_v1alpha08_sonataflowclusterplatform.yaml 
b/test/testdata/sonataflow.org_v1alpha08_sonataflowclusterplatform.yaml
new file mode 100644
index 00000000..9759c15a
--- /dev/null
+++ b/test/testdata/sonataflow.org_v1alpha08_sonataflowclusterplatform.yaml
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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: SonataFlowClusterPlatform
+metadata:
+  name: cluster
+spec:
+  platformRef:
+    name: sonataflow-platform
+    namespace: test-ns
\ No newline at end of file
diff --git a/test/yaml.go b/test/yaml.go
index 37d28220..d1c4006b 100644
--- a/test/yaml.go
+++ b/test/yaml.go
@@ -27,19 +27,15 @@ import (
        "runtime"
        "strings"
 
-       "github.com/davecgh/go-spew/spew"
-       "k8s.io/klog/v2"
-       "sigs.k8s.io/controller-runtime/pkg/client"
-
        "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/log"
+       "github.com/davecgh/go-spew/spew"
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/util/yaml"
-
-       "github.com/apache/incubator-kie-kogito-serverless-operator/log"
-
-       operatorapi 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       "k8s.io/klog/v2"
+       "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 const (
@@ -53,6 +49,7 @@ const (
        sonataFlowPlatformYamlCR                  = 
"sonataflow.org_v1alpha08_sonataflowplatform.yaml"
        sonataFlowPlatformWithCacheMinikubeYamlCR = 
"sonataflow.org_v1alpha08_sonataflowplatform_withCache_minikube.yaml"
        sonataFlowPlatformForOpenshift            = 
"sonataflow.org_v1alpha08_sonataflowplatform_openshift.yaml"
+       sonataFlowClusterPlatformYamlCR           = 
"sonataflow.org_v1alpha08_sonataflowclusterplatform.yaml"
        sonataFlowBuilderConfig                   = 
"sonataflow-operator-builder-config_v1_configmap.yaml"
        sonataFlowBuildSucceed                    = 
"sonataflow.org_v1alpha08_sonataflowbuild.yaml"
 
@@ -86,6 +83,31 @@ func GetKubernetesResource(testFile string, resource 
client.Object) {
        }
 }
 
+func getSonataFlowClusterPlatform(testFile string) 
*operatorapi.SonataFlowClusterPlatform {
+       kscp := &operatorapi.SonataFlowClusterPlatform{}
+       yamlFile, err := os.ReadFile(path.Join(getTestDataDir(), testFile))
+       if err != nil {
+               klog.V(log.E).ErrorS(err, "yamlFile.Get")
+               panic(err)
+       }
+       // Important: Here we are reading the CR deployment file from a given 
path and creating a &operatorapi.SonataFlowPlatform struct
+       err = yaml.NewYAMLOrJSONDecoder(bytes.NewReader(yamlFile), 
100).Decode(kscp)
+       if err != nil {
+               klog.V(log.E).ErrorS(err, "Unmarshal")
+               panic(err)
+       }
+       klog.V(log.D).InfoS("Successfully read KSCP", "kscp", kscp)
+       kscp.Status.Manager().InitializeConditions()
+       return kscp
+}
+
+func GetSonataFlowClusterPlatformInReadyPhase(path string, namespace string) 
*operatorapi.SonataFlowClusterPlatform {
+       kscp := getSonataFlowClusterPlatform(path)
+       kscp.Spec.PlatformRef.Namespace = namespace
+       kscp.Status.Manager().MarkTrue(api.SucceedConditionType)
+       return kscp
+}
+
 func getSonataFlowPlatform(testFile string) *operatorapi.SonataFlowPlatform {
        ksp := &operatorapi.SonataFlowPlatform{}
        yamlFile, err := os.ReadFile(path.Join(getTestDataDir(), testFile))
@@ -193,6 +215,10 @@ func GetBaseSonataFlowWithProdOpsProfile(namespace string) 
*operatorapi.SonataFl
        return NewSonataFlow(SonataFlowSimpleOpsYamlCR, namespace)
 }
 
+func GetBaseClusterPlatformInReadyPhase(namespace string) 
*operatorapi.SonataFlowClusterPlatform {
+       return 
GetSonataFlowClusterPlatformInReadyPhase(sonataFlowClusterPlatformYamlCR, 
namespace)
+}
+
 func GetBasePlatformInReadyPhase(namespace string) 
*operatorapi.SonataFlowPlatform {
        return GetSonataFlowPlatformInReadyPhase(sonataFlowPlatformYamlCR, 
namespace)
 }
@@ -213,6 +239,10 @@ func GetBasePlatformWithDevBaseImageInReadyPhase(namespace 
string) *operatorapi.
        return platform
 }
 
+func GetBaseClusterPlatform() *operatorapi.SonataFlowClusterPlatform {
+       return getSonataFlowClusterPlatform(sonataFlowClusterPlatformYamlCR)
+}
+
 func GetBasePlatform() *operatorapi.SonataFlowPlatform {
        return getSonataFlowPlatform(sonataFlowPlatformYamlCR)
 }


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

Reply via email to