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

wmedvedeo 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 8b25b999 Fix #304 - Operator driven service discovery API (#283)
8b25b999 is described below

commit 8b25b9995744d03ac2cf261b0a98841d1f258df8
Author: Walter Medvedeo <[email protected]>
AuthorDate: Wed Nov 15 13:49:30 2023 +0100

    Fix #304 - Operator driven service discovery API (#283)
    
    * KOGITO-9867: Operator driven service discovery API
    
    * KOGITO-9867: Operator driven service discovery API
        - Code review suggestions 1, simplify the original packages into the 
discovery package
    
    * KOGITO-9867: Operator driven service discovery API
        - Code review suggestions 2
    
    * KOGITO-9867: Operator driven service discovery API
        - Integrate the service discovery API
    
    * KOGITO-9867: Operator driven service discovery API
        - Code review suggestions 3, tests added
---
 controllers/discovery/discovery.go                 | 211 +++++++++++++++++++
 controllers/discovery/discovery_test.go            |  92 +++++++++
 controllers/discovery/knative_catalog.go           |  36 ++++
 controllers/discovery/kubernetes_catalog.go        |  78 +++++++
 controllers/discovery/port_utils.go                |  96 +++++++++
 controllers/discovery/port_utils_test.go           | 127 ++++++++++++
 controllers/discovery/queries.go                   | 102 ++++++++++
 controllers/discovery/queries_test.go              | 225 +++++++++++++++++++++
 controllers/discovery/test_utils.go                | 110 ++++++++++
 controllers/discovery/uri_parser.go                | 149 ++++++++++++++
 controllers/discovery/uri_parser_test.go           | 130 ++++++++++++
 controllers/discovery/uri_utils.go                 | 110 ++++++++++
 controllers/discovery/uri_utils_test.go            | 133 ++++++++++++
 controllers/profiles/common/app_properties.go      | 101 +++++++++
 controllers/profiles/common/app_properties_test.go | 123 +++++++++++
 controllers/profiles/common/mutate_visitors.go     |   7 +-
 .../profiles/common/object_creators_test.go        |   4 +-
 controllers/profiles/common/reconciler.go          |   5 +-
 controllers/profiles/dev/states_dev.go             |   2 +-
 controllers/profiles/prod/deployment_handler.go    |   2 +-
 controllers/profiles/prod/profile_prod.go          |  10 +-
 go.work.sum                                        |   6 -
 testbdd/go.sum                                     |  15 ++
 utils/kubernetes/deployment.go                     |  15 ++
 utils/kubernetes/service.go                        |  15 ++
 25 files changed, 1890 insertions(+), 14 deletions(-)

diff --git a/controllers/discovery/discovery.go 
b/controllers/discovery/discovery.go
new file mode 100644
index 00000000..06c4709f
--- /dev/null
+++ b/controllers/discovery/discovery.go
@@ -0,0 +1,211 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "context"
+       "fmt"
+
+       v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+const (
+       KnativeScheme    = "knative"
+       KubernetesScheme = "kubernetes"
+       OpenshiftScheme  = "openshift"
+
+       // PortLabel well known label name to select a particular target port
+       PortLabel = "port"
+
+       // KubernetesDNSAddress use this output format with kubernetes services 
and pods to resolve to the corresponding
+       // kubernetes DNS name. see: 
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
+       KubernetesDNSAddress = "KubernetesDNSAddress"
+
+       // KubernetesIPAddress default format, resolves objects addresses to 
the corresponding cluster IP address.
+       KubernetesIPAddress = "KubernetesIPAddress"
+
+       // kubernetes groups
+       kubernetesServices     = "kubernetes:services.v1"
+       kubernetesPods         = "kubernetes:pods.v1"
+       kubernetesDeployments  = "kubernetes:deployments.v1.apps"
+       kubernetesStatefulSets = "kubernetes:statefulsets.v1.apps"
+       kubernetesIngresses    = "kubernetes:ingresses.v1.networking.k8s.io"
+
+       // knative groups
+       knativeServices = "knative:services.v1.serving.knative.dev"
+
+       // openshift groups
+       openshiftRoutes            = "openshift:routes.v1.route.openshift.io"
+       openshiftDeploymentConfigs = 
"openshift:deploymentconfigs.v1.apps.openshift.io"
+)
+
+type ResourceUri struct {
+       Scheme       string
+       GVK          v1.GroupVersionKind
+       Namespace    string
+       Name         string
+       CustomLabels map[string]string
+}
+
+// ServiceCatalog is the entry point to resolve resource addresses given a 
ResourceUri.
+type ServiceCatalog interface {
+       // Query returns the address corresponding to the resource identified 
by the uri. In the case of services or pods,
+       // the outputFormat can be used to determine the type of address to 
calculate.
+       // If the outputFormat is KubernetesDNSAddress, the returned value for 
a service will be like this: http://my-service.my-namespace.svc:8080,
+       // and the returned value for pod will be like this: 
http://10-244-1-135.my-namespace.pod.cluster.local:8080.
+       // If the outputFormat is KubernetesIPAddress, the returned value for 
pods and services, and other resource types,
+       // will be like this: http://10.245.1.132:8080
+       Query(ctx context.Context, uri ResourceUri, outputFormat string) 
(string, error)
+}
+
+type sonataFlowServiceCatalog struct {
+       kubernetesCatalog ServiceCatalog
+       knativeCatalog    ServiceCatalog
+       openshiftCatalog  ServiceCatalog
+}
+
+// NewServiceCatalog returns a new ServiceCatalog configured to resolve 
kubernetes, knative, and openshift resource addresses.
+func NewServiceCatalog(cli client.Client) ServiceCatalog {
+       return &sonataFlowServiceCatalog{
+               kubernetesCatalog: newK8SServiceCatalog(cli),
+               knativeCatalog:    newKnServiceCatalog(cli),
+       }
+}
+
+func (c *sonataFlowServiceCatalog) Query(ctx context.Context, uri ResourceUri, 
outputFormat string) (string, error) {
+       switch uri.Scheme {
+       case KubernetesScheme:
+               return c.kubernetesCatalog.Query(ctx, uri, outputFormat)
+       case KnativeScheme:
+               return "", fmt.Errorf("knative service discovery is not yet 
implemened")
+       case OpenshiftScheme:
+               return "", fmt.Errorf("openshift service discovery is not yet 
implemented")
+       default:
+               return "", fmt.Errorf("unknown scheme was provided for service 
discovery: %s", uri.Scheme)
+       }
+}
+
+type ResourceUriBuilder struct {
+       uri *ResourceUri
+}
+
+func NewResourceUriBuilder(scheme string) ResourceUriBuilder {
+       return ResourceUriBuilder{
+               uri: &ResourceUri{
+                       Scheme:       scheme,
+                       GVK:          v1.GroupVersionKind{},
+                       CustomLabels: map[string]string{},
+               },
+       }
+}
+
+func (b ResourceUriBuilder) Kind(kind string) ResourceUriBuilder {
+       b.uri.GVK.Kind = kind
+       return b
+}
+
+func (b ResourceUriBuilder) Version(version string) ResourceUriBuilder {
+       b.uri.GVK.Version = version
+       return b
+}
+
+func (b ResourceUriBuilder) Group(group string) ResourceUriBuilder {
+       b.uri.GVK.Group = group
+       return b
+}
+
+func (b ResourceUriBuilder) Namespace(namespace string) ResourceUriBuilder {
+       b.uri.Namespace = namespace
+       return b
+}
+
+func (b ResourceUriBuilder) Name(name string) ResourceUriBuilder {
+       b.uri.Name = name
+       return b
+}
+
+func (b ResourceUriBuilder) Port(customPort string) ResourceUriBuilder {
+       b.uri.SetPort(customPort)
+       return b
+}
+
+func (b ResourceUriBuilder) WithLabel(labelName string, labelValue string) 
ResourceUriBuilder {
+       b.uri.CustomLabels[labelName] = labelValue
+       return b
+}
+
+func (b ResourceUriBuilder) Build() *ResourceUri {
+       return b.uri
+}
+
+func (r *ResourceUri) AddLabel(name string, value string) *ResourceUri {
+       if len(value) > 0 {
+               r.CustomLabels[name] = value
+       }
+       return r
+}
+
+func (r *ResourceUri) GetLabel(name string) string {
+       if len(name) > 0 {
+               return r.CustomLabels[name]
+       }
+       return ""
+}
+
+func (r *ResourceUri) SetPort(value string) *ResourceUri {
+       return r.AddLabel(PortLabel, value)
+}
+
+func (r *ResourceUri) GetPort() string {
+       return r.GetLabel(PortLabel)
+}
+
+func (r *ResourceUri) String() string {
+       if r == nil {
+               return ""
+       }
+       gvk := appendWithDelimiter("", r.GVK.Kind, ".")
+       gvk = appendWithDelimiter(gvk, r.GVK.Version, ".")
+       gvk = appendWithDelimiter(gvk, r.GVK.Group, ".")
+       uri := r.Scheme + ":" + gvk
+       uri = appendWithDelimiter(uri, r.Namespace, "/")
+       uri = appendWithDelimiter(uri, r.Name, "/")
+
+       return appendWithDelimiter(uri, buildLabelsString(r.CustomLabels, "&"), 
"?")
+}
+
+func appendWithDelimiter(value string, toAppend string, delimiter string) 
string {
+       if len(toAppend) > 0 {
+               if len(value) > 0 {
+                       return fmt.Sprintf("%s%s%s", value, delimiter, toAppend)
+               } else {
+                       return fmt.Sprintf("%s%s", value, toAppend)
+               }
+       }
+       return value
+}
+
+func buildParam(name string, value string) string {
+       return fmt.Sprintf("%s=%s", name, value)
+}
+
+func buildLabelsString(labels map[string]string, delimiter string) string {
+       var labelsStr string
+       for name, value := range labels {
+               labelsStr = appendWithDelimiter(labelsStr, buildParam(name, 
value), delimiter)
+       }
+       return labelsStr
+}
diff --git a/controllers/discovery/discovery_test.go 
b/controllers/discovery/discovery_test.go
new file mode 100644
index 00000000..e54b4685
--- /dev/null
+++ b/controllers/discovery/discovery_test.go
@@ -0,0 +1,92 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "context"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       corev1 "k8s.io/api/core/v1"
+       "sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+func Test_NewResourceUriBuilder(t *testing.T) {
+       resourceUri := NewResourceUriBuilder(KubernetesScheme).
+               Kind("deployments").
+               Group("apps").
+               Version("v1").
+               Namespace(namespace1).
+               Name(service1).
+               Port("custom-port").
+               WithLabel(label1, valueLabel1).Build()
+
+       assert.Equal(t, "deployments", resourceUri.GVK.Kind)
+       assert.Equal(t, "apps", resourceUri.GVK.Group)
+       assert.Equal(t, "v1", resourceUri.GVK.Version)
+       assert.Equal(t, namespace1, resourceUri.Namespace)
+       assert.Equal(t, service1, resourceUri.Name)
+       assert.Equal(t, 2, len(resourceUri.CustomLabels))
+       assert.Equal(t, "custom-port", resourceUri.CustomLabels["port"])
+       assert.Equal(t, valueLabel1, resourceUri.CustomLabels[label1])
+}
+
+func Test_QueryKubernetesServiceDNSMode(t *testing.T) {
+       doTestQueryKubernetesService(t, KubernetesDNSAddress, 
"http://service1.namespace1.svc:80";)
+}
+
+func Test_QueryKubernetesServiceIPAddressMode(t *testing.T) {
+       doTestQueryKubernetesService(t, KubernetesIPAddress, 
"http://10.1.5.18:80";)
+}
+
+func doTestQueryKubernetesService(t *testing.T, outputFormat string, 
expectedUri string) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort(httpProtocolName, tcp, defaultHttp))
+       service.Spec.Type = corev1.ServiceTypeNodePort
+       service.Spec.ClusterIP = "10.1.5.18"
+       cli := fake.NewClientBuilder().WithRuntimeObjects(service).Build()
+       ctg := NewServiceCatalog(cli)
+       doTestQuery(t, ctg, *NewResourceUriBuilder(KubernetesScheme).
+               Kind("services").
+               Version("v1").
+               Namespace(namespace1).
+               Name(service1).Build(), outputFormat, expectedUri)
+}
+
+func Test_QueryKubernetesPodDNSMode(t *testing.T) {
+       doTestQueryKubernetesPod(t, KubernetesDNSAddress, 
"http://10-1-12-13.namespace1.pod:80";)
+}
+
+func Test_QueryKubernetesPodIPAddressMode(t *testing.T) {
+       doTestQueryKubernetesPod(t, KubernetesIPAddress, "http://10.1.12.13:80";)
+}
+
+func doTestQueryKubernetesPod(t *testing.T, outputFormat string, expectedUri 
string) {
+       pod := mockPodWithContainers(namespace1, pod1,
+               *mockContainerWithPorts("container1", 
mockContainerPort(httpProtocolName, tcp, defaultHttp)))
+       pod.Status.PodIP = "10.1.12.13"
+       cli := fake.NewClientBuilder().WithRuntimeObjects(pod).Build()
+       ctg := NewServiceCatalog(cli)
+       doTestQuery(t, ctg, *NewResourceUriBuilder(KubernetesScheme).
+               Kind("pods").
+               Version("v1").
+               Namespace(namespace1).
+               Name(pod1).Build(), outputFormat, expectedUri)
+}
+
+func doTestQuery(t *testing.T, ctg ServiceCatalog, resourceUri ResourceUri, 
outputFormat, expectedUri string) {
+       uri, err := ctg.Query(context.TODO(), resourceUri, outputFormat)
+       assert.NoError(t, err)
+       assert.Equal(t, expectedUri, uri)
+}
diff --git a/controllers/discovery/knative_catalog.go 
b/controllers/discovery/knative_catalog.go
new file mode 100644
index 00000000..ad7fb011
--- /dev/null
+++ b/controllers/discovery/knative_catalog.go
@@ -0,0 +1,36 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "context"
+       "fmt"
+
+       "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type knServiceCatalog struct {
+       Client client.Client
+}
+
+func newKnServiceCatalog(cli client.Client) knServiceCatalog {
+       return knServiceCatalog{
+               Client: cli,
+       }
+}
+
+func (c knServiceCatalog) Query(ctx context.Context, uri ResourceUri, 
outputFormat string) (string, error) {
+       return "", fmt.Errorf("knative service discovery is not yet implemened")
+}
diff --git a/controllers/discovery/kubernetes_catalog.go 
b/controllers/discovery/kubernetes_catalog.go
new file mode 100644
index 00000000..eb526e75
--- /dev/null
+++ b/controllers/discovery/kubernetes_catalog.go
@@ -0,0 +1,78 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "context"
+       "fmt"
+
+       "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+const (
+       serviceKind = "services"
+       podKind     = "pods"
+)
+
+type k8sServiceCatalog struct {
+       Client client.Client
+}
+
+func newK8SServiceCatalog(cli client.Client) k8sServiceCatalog {
+       return k8sServiceCatalog{
+               Client: cli,
+       }
+}
+
+func (c k8sServiceCatalog) Query(ctx context.Context, uri ResourceUri, 
outputFormat string) (string, error) {
+       switch uri.GVK.Kind {
+       case serviceKind:
+               return c.resolveServiceQuery(ctx, uri, outputFormat)
+       case podKind:
+               return c.resolvePodQuery(ctx, uri, outputFormat)
+       default:
+               return "", fmt.Errorf("resolution of kind: %s is not yet 
implemented", uri.GVK.Kind)
+       }
+}
+
+func (c k8sServiceCatalog) resolveServiceQuery(ctx context.Context, uri 
ResourceUri, outputFormat string) (string, error) {
+       if service, err := findService(ctx, c.Client, uri.Namespace, uri.Name); 
err != nil {
+               return "", err
+       } else if serviceUri, err := resolveServiceUri(service, uri.GetPort(), 
outputFormat); err != nil {
+               return "", err
+       } else {
+               return serviceUri, nil
+       }
+}
+
+func (c k8sServiceCatalog) resolvePodQuery(ctx context.Context, uri 
ResourceUri, outputFormat string) (string, error) {
+       if pod, service, err := findPodAndReferenceServiceByPodLabels(ctx, 
c.Client, uri.Namespace, uri.Name); err != nil {
+               return "", err
+       } else {
+               if service != nil {
+                       if serviceUri, err := resolveServiceUri(service, 
uri.GetPort(), outputFormat); err != nil {
+                               return "", err
+                       } else {
+                               return serviceUri, nil
+                       }
+               } else {
+                       if podUri, err := resolvePodUri(pod, "", uri.GetPort(), 
outputFormat); err != nil {
+                               return "", err
+                       } else {
+                               return podUri, nil
+                       }
+               }
+       }
+}
diff --git a/controllers/discovery/port_utils.go 
b/controllers/discovery/port_utils.go
new file mode 100644
index 00000000..891995c6
--- /dev/null
+++ b/controllers/discovery/port_utils.go
@@ -0,0 +1,96 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/utils/kubernetes"
+       corev1 "k8s.io/api/core/v1"
+)
+
+const (
+       httpProtocolName  = "http"
+       httpsProtocolName = "https"
+       webProtocolName   = "web"
+       securePort        = 443
+       appSecurePort     = 8443
+)
+
+func isSecurePort(port int) bool {
+       return port == securePort || port == appSecurePort
+}
+
+// findBestSuitedServicePort returns the best suited ServicePort to connect to 
a service.
+// The optional customPort can be used to determine which port should be used 
for the communication, when not set,
+// the best suited port is returned. For this last, a secure port has 
precedence over a no-secure port.
+func findBestSuitedServicePort(service *corev1.Service, customPort string) 
*corev1.ServicePort {
+       // customPort is provided and is configured?
+       if len(customPort) > 0 {
+               if result, _ := kubernetes.GetServicePortByName(customPort, 
service); result != nil {
+                       return result
+               }
+       }
+       // has ssl port?
+       if result, _ := kubernetes.GetServicePortByName(httpsProtocolName, 
service); result != nil {
+               return result
+       }
+       // has http port?
+       if result, _ := kubernetes.GetServicePortByName(httpProtocolName, 
service); result != nil {
+               return result
+       }
+       // has web port?
+       if result, _ := kubernetes.GetServicePortByName(webProtocolName, 
service); result != nil {
+               return result
+       }
+       // by definition a service must always have at least one port, get the 
first port.
+       return &service.Spec.Ports[0]
+}
+
+func isSecureServicePort(servicePort *corev1.ServicePort) bool {
+       return servicePort.Name == httpsProtocolName || 
isSecurePort(int(servicePort.Port))
+}
+
+// findBestSuitedContainerPort returns the best suited PortPort to connect to 
a pod, or nil if the pod has no ports at all.
+// The optional customPort can be used to determine which port should be used 
for the communication, when not set,
+// the best suited port is returned. For this last, a secure port has 
precedence over a non-secure port.
+func findBestSuitedContainerPort(container *corev1.Container, customPort 
string) *corev1.ContainerPort {
+       // containers with no ports are permitted, we must check.
+       if len(container.Ports) == 0 {
+               return nil
+       }
+       // customPort is provided and configured?
+       if len(customPort) > 0 {
+               if result, _ := kubernetes.GetContainerPortByName(customPort, 
container); result != nil {
+                       return result
+               }
+       }
+       // has ssl port?
+       if result, _ := kubernetes.GetContainerPortByName(httpsProtocolName, 
container); result != nil {
+               return result
+       }
+       // has http port?
+       if result, _ := kubernetes.GetContainerPortByName(httpProtocolName, 
container); result != nil {
+               return result
+       }
+       // has web port?
+       if result, _ := kubernetes.GetContainerPortByName(webProtocolName, 
container); result != nil {
+               return result
+       }
+       // when defined, a ContainerPort must always have containerPort 
(Required value)
+       return &container.Ports[0]
+}
+
+func isSecureContainerPort(containerPort *corev1.ContainerPort) bool {
+       return containerPort.Name == httpsProtocolName || 
isSecurePort(int(containerPort.ContainerPort))
+}
diff --git a/controllers/discovery/port_utils_test.go 
b/controllers/discovery/port_utils_test.go
new file mode 100644
index 00000000..367dc428
--- /dev/null
+++ b/controllers/discovery/port_utils_test.go
@@ -0,0 +1,127 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       corev1 "k8s.io/api/core/v1"
+)
+
+func TestIsSecurePort(t *testing.T) {
+       assert.False(t, isSecurePort(80))
+       assert.False(t, isSecurePort(8080))
+       assert.True(t, isSecurePort(443))
+       assert.True(t, isSecurePort(8443))
+}
+
+func TestBestSuitedServicePort_BestIsCustomPort(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort("not-wanted", tcp, 8282),
+               mockServicePort(httpsProtocolName, tcp, defaultHttps),
+               mockServicePort(customPortName, tcp, defaultHttp))
+       doTestBestSuitedServicePort(t, service, customPortName, 
&service.Spec.Ports[2])
+}
+
+func TestBestSuitedServicePort_BestIsHttpsPort(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort("not-wanted", tcp, 8282),
+               mockServicePort(httpProtocolName, tcp, defaultHttp),
+               mockServicePort(httpsProtocolName, tcp, defaultHttps))
+       doTestBestSuitedServicePort(t, service, "", &service.Spec.Ports[2])
+}
+
+func TestBestSuitedServicePort_BestIsHttpPort(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort("not-wanted", tcp, 8282),
+               mockServicePort(webProtocolName, tcp, 81),
+               mockServicePort(httpProtocolName, tcp, defaultHttp))
+       doTestBestSuitedServicePort(t, service, "", &service.Spec.Ports[2])
+}
+
+func TestBestSuitedServicePort_BestWebPort(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort("not-wanted", tcp, 8282),
+               mockServicePort(webProtocolName, tcp, 81))
+       doTestBestSuitedServicePort(t, service, "", &service.Spec.Ports[1])
+}
+
+func TestBestSuitedServicePort_BestIsFirst(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort("first-port", tcp, 8282),
+               mockServicePort("second-port", tcp, 8383))
+       doTestBestSuitedServicePort(t, service, "", &service.Spec.Ports[0])
+}
+
+func TestIsSecureServicePort(t *testing.T) {
+       servicePort := mockServicePort(httpsProtocolName, tcp, 443)
+       assert.True(t, isSecureServicePort(&servicePort))
+       servicePort = mockServicePort("other-secure-port", tcp, 443)
+       assert.True(t, isSecureServicePort(&servicePort))
+       servicePort = mockServicePort(httpProtocolName, tcp, 80)
+       assert.False(t, isSecureServicePort(&servicePort))
+}
+
+func doTestBestSuitedServicePort(t *testing.T, service *corev1.Service, 
customPort string, expectedPort *corev1.ServicePort) {
+       result := findBestSuitedServicePort(service, customPort)
+       assert.Equal(t, result, expectedPort)
+}
+
+func TestBestSuitedContainerPort_ContainerWithNoPorts(t *testing.T) {
+       doTestBestSuitedContainerPort(t, mockContainerWithPorts(""), "", nil)
+}
+
+func TestBestSuitedContainerPort_BestIsCustomPort(t *testing.T) {
+       container := mockContainerWithPorts("", mockContainerPort("not-wanted", 
tcp, 8282),
+               mockContainerPort(httpsProtocolName, tcp, defaultHttps),
+               mockContainerPort(customPortName, tcp, defaultHttp))
+       doTestBestSuitedContainerPort(t, container, customPortName, 
&container.Ports[2])
+}
+
+func TestBestSuitedContainerPort_BestIsHttpsPort(t *testing.T) {
+       container := mockContainerWithPorts("", mockContainerPort("not-wanted", 
tcp, 8282),
+               mockContainerPort(httpProtocolName, tcp, defaultHttp),
+               mockContainerPort(httpsProtocolName, tcp, defaultHttps))
+       doTestBestSuitedContainerPort(t, container, "", &container.Ports[2])
+}
+
+func TestBestSuitedContainerPort_BestIsHttpPort(t *testing.T) {
+       container := mockContainerWithPorts("", mockContainerPort("not-wanted", 
tcp, 8282),
+               mockContainerPort(webProtocolName, tcp, 81),
+               mockContainerPort(httpProtocolName, tcp, defaultHttp))
+       doTestBestSuitedContainerPort(t, container, "", &container.Ports[2])
+}
+
+func TestBestSuitedContainerPort_BestWebPort(t *testing.T) {
+       container := mockContainerWithPorts("", mockContainerPort("not-wanted", 
tcp, 8282),
+               mockContainerPort(webProtocolName, tcp, 81))
+       doTestBestSuitedContainerPort(t, container, "", &container.Ports[1])
+}
+
+func TestBestSuitedContainerPort_BestIsFirst(t *testing.T) {
+       container := mockContainerWithPorts("", mockContainerPort("first-port", 
tcp, 8282),
+               mockContainerPort("second-port", tcp, 8383))
+       doTestBestSuitedContainerPort(t, container, "", &container.Ports[0])
+}
+
+func doTestBestSuitedContainerPort(t *testing.T, container *corev1.Container, 
customPort string, expectedPort *corev1.ContainerPort) {
+       result := findBestSuitedContainerPort(container, customPort)
+       assert.Equal(t, result, expectedPort)
+}
+
+func TestIsSecureContainerPort(t *testing.T) {
+       containerPort := mockContainerPort(httpsProtocolName, tcp, 443)
+       assert.True(t, isSecureContainerPort(&containerPort))
+       containerPort = mockContainerPort("other-secure-port", tcp, 443)
+       assert.True(t, isSecureContainerPort(&containerPort))
+       containerPort = mockContainerPort(httpProtocolName, tcp, 80)
+       assert.False(t, isSecureContainerPort(&containerPort))
+}
diff --git a/controllers/discovery/queries.go b/controllers/discovery/queries.go
new file mode 100644
index 00000000..5710201d
--- /dev/null
+++ b/controllers/discovery/queries.go
@@ -0,0 +1,102 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "context"
+
+       appsv1 "k8s.io/api/apps/v1"
+       corev1 "k8s.io/api/core/v1"
+       networkingV1 "k8s.io/api/networking/v1"
+       "k8s.io/apimachinery/pkg/types"
+       "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+const podTemplateHashLabel = "pod-template-hash"
+
+// findService finds a service by name in the given namespace.
+func findService(ctx context.Context, cli client.Client, namespace string, 
name string) (*corev1.Service, error) {
+       service := &corev1.Service{}
+       if err := cli.Get(ctx, types.NamespacedName{Namespace: namespace, Name: 
name}, service); err != nil {
+               return nil, err
+       }
+       return service, nil
+}
+
+// findServiceByLabels finds a service by a set of matching labels in the 
given namespace.
+func findServiceByLabels(ctx context.Context, cli client.Client, namespace 
string, labels map[string]string) (*corev1.ServiceList, error) {
+       serviceList := &corev1.ServiceList{}
+       if err := cli.List(ctx, serviceList, client.InNamespace(namespace), 
client.MatchingLabels(labels)); err != nil {
+               return nil, err
+       }
+       return serviceList, nil
+}
+
+// findPod finds a pod by name in the given namespace.
+func findPod(ctx context.Context, cli client.Client, namespace string, name 
string) (*corev1.Pod, error) {
+       pod := &corev1.Pod{}
+       if err := cli.Get(ctx, types.NamespacedName{Namespace: namespace, Name: 
name}, pod); err != nil {
+               return nil, err
+       }
+       return pod, nil
+}
+
+// findPodAndReferenceServiceByPodLabels finds a pod by name in the given 
namespace at the same time it piggybacks it's
+// reference service if any. The reference service is determined by using the 
same set of labels as the pod.
+func findPodAndReferenceServiceByPodLabels(ctx context.Context, cli 
client.Client, namespace string, name string) (*corev1.Pod, *corev1.Service, 
error) {
+       if pod, err := findPod(ctx, cli, namespace, name); err != nil {
+               return nil, nil, err
+       } else {
+               queryLabels := pod.Labels
+               // pod-template-hash is pod dependent, mustn't be considered.
+               delete(queryLabels, podTemplateHashLabel)
+               if len(queryLabels) > 0 {
+                       // check if we have a defined reference service
+                       if serviceList, err := findServiceByLabels(ctx, cli, 
namespace, queryLabels); err != nil {
+                               return nil, nil, err
+                       } else if len(serviceList.Items) > 0 {
+                               return pod, &serviceList.Items[0], nil
+                       }
+               }
+               return pod, nil, nil
+       }
+}
+
+// findDeployment finds a deployment by name in the given namespace.
+func findDeployment(ctx context.Context, cli client.Client, namespace string, 
name string) (*appsv1.Deployment, error) {
+       deployment := &appsv1.Deployment{}
+       if err := cli.Get(ctx, types.NamespacedName{Namespace: namespace, Name: 
name}, deployment); err != nil {
+               return nil, err
+       }
+       return deployment, nil
+}
+
+// findStatefulSet finds a stateful set by name in the given namespace.
+func findStatefulSet(ctx context.Context, cli client.Client, namespace string, 
name string) (*appsv1.StatefulSet, error) {
+       statefulSet := &appsv1.StatefulSet{}
+       if err := cli.Get(ctx, types.NamespacedName{Namespace: namespace, Name: 
name}, statefulSet); err != nil {
+               return nil, err
+       }
+       return statefulSet, nil
+}
+
+// findIngress finds an ingress by name in the given namespace.
+func findIngress(ctx context.Context, cli client.Client, namespace string, 
name string) (*networkingV1.Ingress, error) {
+       ingress := &networkingV1.Ingress{}
+       if err := cli.Get(ctx, types.NamespacedName{Namespace: namespace, Name: 
name}, ingress); err != nil {
+               return nil, err
+       }
+       return ingress, nil
+}
diff --git a/controllers/discovery/queries_test.go 
b/controllers/discovery/queries_test.go
new file mode 100644
index 00000000..4c130407
--- /dev/null
+++ b/controllers/discovery/queries_test.go
@@ -0,0 +1,225 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "context"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       appsv1 "k8s.io/api/apps/v1"
+       corev1 "k8s.io/api/core/v1"
+       networkingV1 "k8s.io/api/networking/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+func Test_findService(t *testing.T) {
+       service := mockService1(nil)
+       cli := fake.NewClientBuilder().WithRuntimeObjects(service).Build()
+       result, err := findService(context.TODO(), cli, namespace1, service1)
+
+       assert.NoError(t, err)
+       assert.Equal(t, service, result)
+}
+
+func Test_findServiceNotFound(t *testing.T) {
+       cli := fake.NewClientBuilder().Build()
+       _, err := findService(context.TODO(), cli, namespace1, service1)
+       assert.ErrorContains(t, err, "\"service1\" not found")
+}
+
+func Test_findServiceByLabels(t *testing.T) {
+       labels := &map[string]string{
+               label1: valueLabel1,
+               label2: valueLabel2,
+       }
+       service := mockService1(labels)
+       cli := fake.NewClientBuilder().WithRuntimeObjects(service).Build()
+       serviceList, err := findServiceByLabels(context.TODO(), cli, 
namespace1, *labels)
+
+       assert.NoError(t, err)
+       assert.Len(t, serviceList.Items, 1)
+       assert.Equal(t, service, &serviceList.Items[0])
+}
+
+func Test_findServiceByLabelsNotFound(t *testing.T) {
+       labels := &map[string]string{
+               label1: valueLabel1,
+               label2: valueLabel2,
+       }
+       queryLabels := map[string]string{
+               label1: valueLabel1,
+       }
+       service := mockService1(labels)
+       cli := fake.NewClientBuilder().WithRuntimeObjects(service).Build()
+       serviceList, err := findServiceByLabels(context.TODO(), cli, 
namespace1, queryLabels)
+
+       assert.NoError(t, err)
+       assert.Len(t, serviceList.Items, 1)
+       assert.Equal(t, service, &serviceList.Items[0])
+}
+
+func Test_findPod(t *testing.T) {
+       pod := mockPod1(nil)
+       cli := fake.NewClientBuilder().WithRuntimeObjects(pod).Build()
+       result, err := findPod(context.TODO(), cli, namespace1, pod1)
+
+       assert.NoError(t, err)
+       assert.Equal(t, pod, result)
+}
+
+func Test_findPodNotFound(t *testing.T) {
+       cli := fake.NewClientBuilder().Build()
+       _, err := findPod(context.TODO(), cli, namespace1, pod1)
+       assert.ErrorContains(t, err, "\"pod1\" not found")
+}
+
+func Test_findPodAndReferenceServiceByPodLabelsWithReferenceService(t 
*testing.T) {
+       podLabels := &map[string]string{
+               label1: valueLabel1,
+               label2: valueLabel2,
+       }
+       service := mockService1(podLabels)
+       pod := mockPod1(podLabels)
+       cli := fake.NewClientBuilder().WithRuntimeObjects(service, pod).Build()
+       resultPod, resultService, err := 
findPodAndReferenceServiceByPodLabels(context.TODO(), cli, namespace1, pod1)
+       assert.NoError(t, err)
+       assert.Equal(t, pod, resultPod)
+       assert.Equal(t, service, resultService)
+}
+
+func Test_findPodAndReferenceServiceByPodLabelsWithoutReferenceService(t 
*testing.T) {
+       podLabels := &map[string]string{
+               label1: valueLabel1,
+               label2: valueLabel2,
+       }
+       serviceLabels := &map[string]string{
+               label1: valueLabel1,
+       }
+       service := mockService1(serviceLabels)
+       pod := mockPod1(podLabels)
+       cli := fake.NewClientBuilder().WithRuntimeObjects(service, pod).Build()
+       resultPod, resultService, err := 
findPodAndReferenceServiceByPodLabels(context.TODO(), cli, namespace1, pod1)
+       assert.NoError(t, err)
+       assert.Equal(t, pod, resultPod)
+       assert.Nil(t, resultService)
+}
+
+func Test_findPodAndReferenceServiceByPodLabelsNotFound(t *testing.T) {
+       cli := fake.NewClientBuilder().Build()
+       resultPod, resultService, err := 
findPodAndReferenceServiceByPodLabels(context.TODO(), cli, namespace1, pod1)
+       assert.ErrorContains(t, err, "\"pod1\" not found")
+       assert.Nil(t, resultPod)
+       assert.Nil(t, resultService)
+}
+
+func Test_findDeployment(t *testing.T) {
+       deployment := mockDeployment1(nil)
+       cli := fake.NewClientBuilder().WithRuntimeObjects(deployment).Build()
+       result, err := findDeployment(context.TODO(), cli, namespace1, 
deployment1)
+
+       assert.NoError(t, err)
+       assert.Equal(t, deployment, result)
+}
+
+func Test_findDeploymentNotFound(t *testing.T) {
+       cli := fake.NewClientBuilder().Build()
+       _, err := findDeployment(context.TODO(), cli, namespace1, deployment1)
+       assert.ErrorContains(t, err, "\"deployment1\" not found")
+}
+
+func Test_findStatefulSet(t *testing.T) {
+       statefulSet := mockStatefulSet1()
+       cli := fake.NewClientBuilder().WithRuntimeObjects(statefulSet).Build()
+       result, err := findStatefulSet(context.TODO(), cli, namespace1, 
statefulSet1)
+
+       assert.NoError(t, err)
+       assert.Equal(t, statefulSet, result)
+}
+
+func Test_findStatefulSetNotFound(t *testing.T) {
+       cli := fake.NewClientBuilder().Build()
+       _, err := findStatefulSet(context.TODO(), cli, namespace1, statefulSet1)
+       assert.ErrorContains(t, err, "\"statefulSet1\" not found")
+}
+
+func Test_findIngress(t *testing.T) {
+       ingress := mockIngress1()
+       cli := fake.NewClientBuilder().WithRuntimeObjects(ingress).Build()
+       result, err := findIngress(context.TODO(), cli, namespace1, ingress1)
+
+       assert.NoError(t, err)
+       assert.Equal(t, ingress, result)
+}
+
+func Test_findIngressNotFound(t *testing.T) {
+       cli := fake.NewClientBuilder().Build()
+       _, err := findIngress(context.TODO(), cli, namespace1, ingress1)
+       assert.ErrorContains(t, err, "\"ingress1\" not found")
+}
+
+func mockService1(labels *map[string]string) *corev1.Service {
+       return mockService(namespace1, service1, labels)
+}
+
+func mockPod1(labels *map[string]string) *corev1.Pod {
+       return mockPod(namespace1, pod1, labels)
+}
+
+func mockDeployment1(labels *map[string]string) *appsv1.Deployment {
+       deployment := &appsv1.Deployment{
+               TypeMeta: metav1.TypeMeta{
+                       Kind:       "Deployment",
+                       APIVersion: "apps/v1",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: namespace1,
+                       Name:      deployment1,
+               },
+       }
+       if labels != nil {
+               deployment.ObjectMeta.Labels = *labels
+       }
+       return deployment
+}
+
+func mockStatefulSet1() *appsv1.StatefulSet {
+       statefulSet := &appsv1.StatefulSet{
+               TypeMeta: metav1.TypeMeta{
+                       Kind:       "StatefulSet",
+                       APIVersion: "apps/v1",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: namespace1,
+                       Name:      statefulSet1,
+               },
+       }
+       return statefulSet
+}
+
+func mockIngress1() *networkingV1.Ingress {
+       ingress := &networkingV1.Ingress{
+               TypeMeta: metav1.TypeMeta{
+                       Kind:       "Ingress",
+                       APIVersion: "networking.k8s.io/v1",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: namespace1,
+                       Name:      ingress1,
+               },
+       }
+       return ingress
+}
diff --git a/controllers/discovery/test_utils.go 
b/controllers/discovery/test_utils.go
new file mode 100644
index 00000000..d56bfb05
--- /dev/null
+++ b/controllers/discovery/test_utils.go
@@ -0,0 +1,110 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       corev1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+const (
+       namespace1   = "namespace1"
+       service1     = "service1"
+       deployment1  = "deployment1"
+       statefulSet1 = "statefulSet1"
+       pod1         = "pod1"
+       container1   = "container1"
+       container2   = "container2"
+       ingress1     = "ingress1"
+       label1       = "label1"
+       valueLabel1  = "valueLabel1"
+       label2       = "label2"
+       valueLabel2  = "valueLabel2"
+
+       customPortName = "my-custom-port"
+       defaultHttp    = 80
+       defaultHttps   = 443
+       tcp            = "TCP"
+)
+
+func mockService(namespace string, name string, labels *map[string]string) 
*corev1.Service {
+       service := &corev1.Service{
+               TypeMeta: metav1.TypeMeta{
+                       Kind:       "Service",
+                       APIVersion: "v1",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: namespace,
+                       Name:      name,
+               },
+       }
+       if labels != nil {
+               service.ObjectMeta.Labels = *labels
+       }
+       return service
+}
+
+func mockServiceWithPorts(namespace string, name string, ports 
...corev1.ServicePort) *corev1.Service {
+       service := mockService(namespace, name, &map[string]string{})
+       service.Spec.Ports = ports
+       return service
+}
+
+func mockServicePort(name string, protocol string, port int32) 
corev1.ServicePort {
+       return corev1.ServicePort{
+               Name:     name,
+               Protocol: corev1.Protocol(protocol),
+               Port:     port,
+       }
+}
+
+func mockPod(namespace string, name string, labels *map[string]string) 
*corev1.Pod {
+       pod := &corev1.Pod{
+               TypeMeta: metav1.TypeMeta{
+                       Kind:       "Pod",
+                       APIVersion: "v1",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: namespace,
+                       Name:      name,
+               },
+       }
+       if labels != nil {
+               pod.ObjectMeta.Labels = *labels
+       }
+       return pod
+}
+
+func mockPodWithContainers(namespace string, name string, containers 
...corev1.Container) *corev1.Pod {
+       pod := mockPod(namespace, name, &map[string]string{})
+       pod.Spec.Containers = containers
+       return pod
+}
+
+func mockContainerWithPorts(name string, ports ...corev1.ContainerPort) 
*corev1.Container {
+       return &corev1.Container{
+               Name:  name,
+               Ports: ports,
+       }
+}
+
+func mockContainerPort(name string, protocol string, port int32) 
corev1.ContainerPort {
+       return corev1.ContainerPort{
+               Name:          name,
+               HostPort:      0,
+               ContainerPort: port,
+               Protocol:      corev1.Protocol(protocol),
+       }
+}
diff --git a/controllers/discovery/uri_parser.go 
b/controllers/discovery/uri_parser.go
new file mode 100644
index 00000000..4f7425c6
--- /dev/null
+++ b/controllers/discovery/uri_parser.go
@@ -0,0 +1,149 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "fmt"
+       "regexp"
+       "strings"
+
+       v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+const (
+       // valid namespace, name, or label name.
+       dns1123LabelFmt         string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
+       namespaceAndNamePattern        = "^/((" + dns1123LabelFmt + ")+)(/(" + 
dns1123LabelFmt + ")+)?"
+       queryStringPattern             = "^(\\?((" + dns1123LabelFmt + ")+\\=(" 
+ dns1123LabelFmt + ")+)" +
+               "(&(" + dns1123LabelFmt + ")+\\=(" + dns1123LabelFmt + ")+)*)?$"
+
+       kubernetesGroupsPattern = "^(" + kubernetesServices +
+               "|" + kubernetesPods +
+               "|" + kubernetesDeployments +
+               "|" + kubernetesStatefulSets +
+               "|" + kubernetesIngresses + ")"
+
+       knativeGroupsPattern = "^(" + knativeServices + ")"
+
+       openshiftGroupsPattern = "^(" + openshiftDeploymentConfigs +
+               "|" + openshiftRoutes + ")"
+)
+
+var kubernetesGroupsExpr = regexp.MustCompile(kubernetesGroupsPattern)
+var knativeGroupsExpr = regexp.MustCompile(knativeGroupsPattern)
+var openshiftGroupsExpr = regexp.MustCompile(openshiftGroupsPattern)
+var namespaceAndNameExpr = regexp.MustCompile(namespaceAndNamePattern)
+var queryStringExpr = regexp.MustCompile(queryStringPattern)
+
+func ParseUri(uri string) (*ResourceUri, error) {
+       if split := kubernetesGroupsExpr.Split(uri, -1); len(split) == 2 {
+               return parseKubernetesUri(uri, 
kubernetesGroupsExpr.FindString(uri), split[1])
+       } else if split := knativeGroupsExpr.Split(uri, -1); len(split) == 2 {
+               return parseKnativeUri(knativeGroupsExpr.FindString(uri), 
split[1])
+       } else if split := openshiftGroupsExpr.Split(uri, -1); len(split) == 2 {
+               return parseOpenshiftUri(openshiftGroupsExpr.FindString(uri), 
split[1])
+       }
+       return nil, fmt.Errorf("invalid uri: %s, not correspond to any of the 
available schemes format: %s, %s, %s", uri, KubernetesScheme, KnativeScheme, 
OpenshiftScheme)
+}
+
+func parseKubernetesUri(uri string, schemaAndGroup string, after string) 
(*ResourceUri, error) {
+       if split := namespaceAndNameExpr.Split(after, -1); len(split) == 2 {
+               namespaceAndName := namespaceAndNameExpr.FindString(after)
+               namespaceAndNameSplit := strings.Split(namespaceAndName, "/")
+               var namespace, name string
+               if len(namespaceAndNameSplit) == 3 {
+                       namespace = namespaceAndNameSplit[1]
+                       name = namespaceAndNameSplit[2]
+               } else {
+                       name = namespaceAndNameSplit[1]
+               }
+               var queryParams map[string]string
+               var err error
+               if queryParams, err = parseQueryParams(uri, split[1]); err != 
nil {
+                       return nil, err
+               }
+
+               gvk, _ := parseGVK(schemaAndGroup)
+               return &ResourceUri{
+                       Scheme:       KubernetesScheme,
+                       GVK:          *gvk,
+                       Namespace:    namespace,
+                       Name:         name,
+                       CustomLabels: queryParams,
+               }, nil
+
+       } else {
+               return nil, fmt.Errorf("invalid kubernetes uri: %s, provided 
namespace, name, or query parameters %s not correspond to the expected formats: 
/my-namespace/my-service?label-name=label-value&another-label=another-value", 
uri, after)
+       }
+}
+
+func parseQueryParams(uri string, queryParams string) (map[string]string, 
error) {
+       result := make(map[string]string)
+       if len(queryParams) > 0 {
+               if !queryStringExpr.MatchString(queryParams) {
+                       return nil, fmt.Errorf("invalid uri: %s, provided query 
string: %s not correspond to the expeced format: 
?label-name=label-value&another-label=another-value", uri, queryParams)
+               } else {
+                       queryParamsTerms := strings.Split(queryParams[1:], "&")
+                       for _, term := range queryParamsTerms {
+                               termSplit := strings.Split(term, "=")
+                               result[termSplit[0]] = termSplit[1]
+                       }
+               }
+       }
+       return result, nil
+}
+
+func parseGVK(schemaGvk string) (*v1.GroupVersionKind, error) {
+       switch schemaGvk {
+       case kubernetesServices:
+               return &v1.GroupVersionKind{
+                       Version: "v1",
+                       Kind:    "services",
+               }, nil
+       case kubernetesPods:
+               return &v1.GroupVersionKind{
+                       Version: "v1",
+                       Kind:    "pods",
+               }, nil
+       case kubernetesDeployments:
+               return &v1.GroupVersionKind{
+                       Group:   "apps",
+                       Version: "v1",
+                       Kind:    "deployments",
+               }, nil
+       case kubernetesStatefulSets:
+               return &v1.GroupVersionKind{
+                       Group:   "apps",
+                       Version: "v1",
+                       Kind:    "statefulsets",
+               }, nil
+       case kubernetesIngresses:
+               return &v1.GroupVersionKind{
+                       Group:   "networking.k8s.io",
+                       Version: "v1",
+                       Kind:    "ingresses",
+               }, nil
+       default:
+               return nil, fmt.Errorf("unknown schema and gvk: %s", schemaGvk)
+       }
+}
+
+func parseKnativeUri(group string, after string) (*ResourceUri, error) {
+       return nil, fmt.Errorf("knative is parsing not yet implemented")
+}
+
+func parseOpenshiftUri(findString string, s string) (*ResourceUri, error) {
+       return nil, fmt.Errorf("openshit is parsing not yet implemented")
+}
diff --git a/controllers/discovery/uri_parser_test.go 
b/controllers/discovery/uri_parser_test.go
new file mode 100644
index 00000000..32cb158b
--- /dev/null
+++ b/controllers/discovery/uri_parser_test.go
@@ -0,0 +1,130 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+var KubernetesServicesTestValues = map[string]*ResourceUri{
+       "kubernetes:services.v1": nil,
+
+       "kubernetes:services.v1/": nil,
+
+       "kubernetes:services.v1/my-service": 
NewResourceUriBuilder(KubernetesScheme).
+               Kind("services").
+               Version("v1").
+               Name("my-service").Build(),
+
+       "kubernetes:services.v1/my-service?": nil,
+
+       "kubernetes:services.v1/my-service?label-a": nil,
+
+       "kubernetes:services.v1/my-service?label-a=": nil,
+
+       "kubernetes:services.v1/my-service?label-a=value-a": 
NewResourceUriBuilder(KubernetesScheme).
+               Kind("services").
+               Version("v1").
+               Name("my-service").
+               WithLabel("label-a", "value-a").Build(),
+
+       "kubernetes:services.v1/my-service?label-a=value-a&": nil,
+
+       "kubernetes:services.v1/my-service?label-a=value-a&label-b": nil,
+
+       "kubernetes:services.v1/my-service?label-a=value-a&label-b=": nil,
+
+       "kubernetes:services.v1/my-service?label-a=value-a&label-b=value-b": 
NewResourceUriBuilder(KubernetesScheme).
+               Kind("services").
+               Version("v1").
+               Name("my-service").
+               WithLabel("label-a", "value-a").
+               WithLabel("label-b", "value-b").Build(),
+
+       "kubernetes:services.v1/my-namespace/": nil,
+
+       "kubernetes:services.v1/my-namespace/my-service": 
NewResourceUriBuilder(KubernetesScheme).
+               Kind("services").
+               Version("v1").
+               Namespace("my-namespace").
+               Name("my-service").
+               Build(),
+
+       "kubernetes:services.v1/my-namespace/my-service/": nil,
+
+       "kubernetes:services.v1/my-namespace/my-service/another": nil,
+
+       "kubernetes:services.v1/my-namespace/my-service?label-a": nil,
+
+       "kubernetes:services.v1/my-namespace/my-service?label-a=": nil,
+
+       "kubernetes:services.v1/my-namespace/my-service?label-a=value-a": 
NewResourceUriBuilder(KubernetesScheme).
+               Kind("services").
+               Version("v1").
+               Namespace("my-namespace").
+               Name("my-service").
+               WithLabel("label-a", "value-a").Build(),
+
+       "kubernetes:services.v1/my-namespace/my-service?label-a=value-a&": nil,
+
+       
"kubernetes:services.v1/my-namespace/my-service?label-a=value-a&label-b": nil,
+
+       
"kubernetes:services.v1/my-namespace/my-service?label-a=value-a&label-b=": nil,
+
+       
"kubernetes:services.v1/my-namespace/my-service?label-a=value-a&label-b=value-b":
 NewResourceUriBuilder(KubernetesScheme).
+               Kind("services").
+               Version("v1").
+               Namespace("my-namespace").
+               Name("my-service").
+               WithLabel("label-a", "value-a").
+               WithLabel("label-b", "value-b").Build(),
+}
+
+func TestParseKubernetesServicesURI(t *testing.T) {
+       for k, v := range KubernetesServicesTestValues {
+               doTestParseKubernetesServicesURI(t, k, v)
+       }
+}
+
+func doTestParseKubernetesServicesURI(t *testing.T, url string, expectedUri 
*ResourceUri) {
+       result, err := ParseUri(url)
+       if expectedUri == nil {
+               if result != nil {
+                       assert.Nil(t, result, "parsing of url: %s should have 
failed, but returned: %s", url, result.String())
+               }
+               assert.Error(t, err, "parsing of url: %s should have failed", 
url)
+       } else {
+               assertEquals(t, result, expectedUri)
+       }
+}
+
+func assertEquals(t *testing.T, uri *ResourceUri, expectedUri *ResourceUri) {
+       assert.NotNil(t, uri, "uri can not be nil")
+       assert.NotNil(t, expectedUri, "expectedUri can not be nil")
+       assert.Equal(t, uri.Scheme, expectedUri.Scheme)
+       assert.Equal(t, uri.Namespace, expectedUri.Namespace)
+       assert.Equal(t, uri.Name, expectedUri.Name)
+       assert.Equal(t, uri.GetPort(), expectedUri.GetPort())
+       assert.Equal(t, uri.GVK.Group, expectedUri.GVK.Group)
+       assert.Equal(t, uri.GVK.Version, expectedUri.GVK.Version)
+       assert.Equal(t, uri.GVK.Kind, expectedUri.GVK.Kind)
+       assert.Equal(t, len(uri.CustomLabels), len(expectedUri.CustomLabels))
+       for k, v := range uri.CustomLabels {
+               assert.True(t, len(expectedUri.CustomLabels[k]) > 0, "label %s 
is not present in expectedUri: %s", k, expectedUri.String())
+               assert.Equal(t, v, expectedUri.CustomLabels[k], "value for 
label %s in expectedUri should be %s, but is %s", k, v, 
expectedUri.CustomLabels[k])
+       }
+}
diff --git a/controllers/discovery/uri_utils.go 
b/controllers/discovery/uri_utils.go
new file mode 100644
index 00000000..4c1de70e
--- /dev/null
+++ b/controllers/discovery/uri_utils.go
@@ -0,0 +1,110 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "fmt"
+       "strings"
+
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/utils/kubernetes"
+       corev1 "k8s.io/api/core/v1"
+)
+
+func resolveServiceUri(service *corev1.Service, customPort string, 
outputFormat string) (string, error) {
+       var port int
+       var protocol string
+       var host string
+       var err error = nil
+
+       switch service.Spec.Type {
+       case corev1.ServiceTypeExternalName:
+               // ExternalName may not work properly with SSL:
+               // 
https://kubernetes.io/docs/concepts/services-networking/service/#externalname
+               protocol = httpProtocolName
+               host = service.Spec.ExternalName
+               port = 80
+       case corev1.ServiceTypeClusterIP:
+               protocol, host, port = 
resolveClusterIPOrTypeNodeServiceUriParams(service, customPort)
+       case corev1.ServiceTypeNodePort:
+               protocol, host, port = 
resolveClusterIPOrTypeNodeServiceUriParams(service, customPort)
+       case corev1.ServiceTypeLoadBalancer:
+               err = fmt.Errorf("Service type %s is not yet supported", 
service.Spec.Type)
+       default:
+               err = fmt.Errorf("Service type %s is not yet supported", 
service.Spec.Type)
+       }
+       if err != nil {
+               return "", err
+       }
+       if service.Spec.Type == corev1.ServiceTypeExternalName || outputFormat 
== KubernetesIPAddress {
+               return buildURI(protocol, host, port), nil
+       } else {
+               return buildKubernetesServiceDNSUri(protocol, 
service.Namespace, service.Name, port), nil
+       }
+}
+
+// resolveClusterIPOrTypeNodeServiceUriParams returns the uri parameters for a 
service of type ClusterIP or TypeNode.
+// The optional customPort can be used to determine which port should be used 
for the communication, when not set,
+// the best suited port is returned. For this last, a secure port has 
precedence over a no-secure port.
+func resolveClusterIPOrTypeNodeServiceUriParams(service *corev1.Service, 
customPort string) (protocol string, host string, port int) {
+       servicePort := findBestSuitedServicePort(service, customPort)
+       if isSecureServicePort(servicePort) {
+               protocol = httpsProtocolName
+       } else {
+               protocol = httpProtocolName
+       }
+       host = service.Spec.ClusterIP
+       port = int(servicePort.Port)
+       return protocol, host, port
+}
+
+func resolvePodUri(pod *corev1.Pod, customContainer string, customPort string, 
outputFormat string) (string, error) {
+       if podIp := pod.Status.PodIP; len(podIp) == 0 {
+               return "", fmt.Errorf("pod: %s in namespace: %s, has no 
allocated address", pod.Name, pod.Namespace)
+       } else {
+               var container *corev1.Container
+               if len(customContainer) > 0 {
+                       container, _ = 
kubernetes.GetContainerByName(customContainer, &pod.Spec)
+               }
+               if container == nil {
+                       container = &pod.Spec.Containers[0]
+               }
+               if containerPort := findBestSuitedContainerPort(container, 
customPort); containerPort == nil {
+                       return "", fmt.Errorf("no container port was found for 
pod: %s in namespace: %s", pod.Name, pod.Namespace)
+               } else {
+                       protocol := httpProtocolName
+                       if isSecure := isSecureContainerPort(containerPort); 
isSecure {
+                               protocol = httpsProtocolName
+                       }
+                       if outputFormat == KubernetesDNSAddress {
+                               return buildKubernetesPodDNSUri(protocol, 
pod.Namespace, podIp, int(containerPort.ContainerPort)), nil
+                       } else {
+                               return buildURI(protocol, podIp, 
int(containerPort.ContainerPort)), nil
+                       }
+               }
+       }
+}
+
+func buildURI(scheme string, host string, port int) string {
+       return fmt.Sprintf("%s://%s:%v", scheme, host, port)
+}
+
+func buildKubernetesServiceDNSUri(scheme string, namespace string, name 
string, port int) string {
+       return fmt.Sprintf("%s://%s.%s.svc:%v", scheme, name, namespace, port)
+}
+
+func buildKubernetesPodDNSUri(scheme string, namespace string, podIP string, 
port int) string {
+       hyphenedIp := strings.Replace(podIP, ".", "-", -1)
+       return fmt.Sprintf("%s://%s.%s.pod:%v", scheme, hyphenedIp, namespace, 
port)
+}
diff --git a/controllers/discovery/uri_utils_test.go 
b/controllers/discovery/uri_utils_test.go
new file mode 100644
index 00000000..6c5fa703
--- /dev/null
+++ b/controllers/discovery/uri_utils_test.go
@@ -0,0 +1,133 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discovery
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       corev1 "k8s.io/api/core/v1"
+)
+
+func Test_resolveServiceUriClusterIPServiceDNSMode(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort(httpProtocolName, tcp, defaultHttp))
+       doTestResolveServiceUri(t, service, corev1.ServiceTypeClusterIP, 
KubernetesDNSAddress, "http://service1.namespace1.svc:80";)
+}
+
+func Test_resolveServiceUriClusterIPServiceIPAddressMode(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort(httpProtocolName, tcp, defaultHttp))
+       service.Spec.ClusterIP = "10.1.15.16"
+       doTestResolveServiceUri(t, service, corev1.ServiceTypeClusterIP, 
KubernetesIPAddress, "http://10.1.15.16:80";)
+}
+
+func Test_resolveServiceUriNodeTypeServiceDNSMode(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort(httpProtocolName, tcp, defaultHttp))
+       doTestResolveServiceUri(t, service, corev1.ServiceTypeNodePort, 
KubernetesDNSAddress, "http://service1.namespace1.svc:80";)
+}
+
+func Test_resolveServiceUriNodeTypeServiceIPAddressMode(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort(httpProtocolName, tcp, defaultHttp))
+       service.Spec.ClusterIP = "10.1.15.16"
+       doTestResolveServiceUri(t, service, corev1.ServiceTypeNodePort, 
KubernetesIPAddress, "http://10.1.15.16:80";)
+}
+
+func Test_resolveServiceUriExternalNameServiceDNSMode(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort(httpProtocolName, tcp, defaultHttp))
+       service.Spec.ExternalName = "external.service.com"
+       doTestResolveServiceUri(t, service, corev1.ServiceTypeExternalName, 
KubernetesIPAddress, "http://external.service.com:80";)
+}
+
+func Test_resolveServiceUriExternalNameServiceIPAddressMode(t *testing.T) {
+       service := mockServiceWithPorts(namespace1, service1, 
mockServicePort(httpProtocolName, tcp, defaultHttp))
+       service.Spec.ExternalName = "external.service.com"
+       doTestResolveServiceUri(t, service, corev1.ServiceTypeExternalName, 
KubernetesIPAddress, "http://external.service.com:80";)
+}
+
+func doTestResolveServiceUri(t *testing.T, service *corev1.Service, 
serviceType corev1.ServiceType, outputMode string, expectedUri string) {
+       service.Spec.Type = serviceType
+       result, err := resolveServiceUri(service, "", outputMode)
+       assert.NoError(t, err)
+       assert.Equal(t, expectedUri, result)
+}
+
+func Test_resolvePodUriDNSMode(t *testing.T) {
+       pod := mockPodWithContainers(namespace1, pod1,
+               *mockContainerWithPorts(container1, 
mockContainerPort(httpProtocolName, tcp, defaultHttp)),
+               *mockContainerWithPorts(container2, 
mockContainerPort(httpsProtocolName, tcp, defaultHttps)))
+       pod.Status.PodIP = "10.1.15.16"
+       doTestResolvePodUri(t, pod, "", "", KubernetesDNSAddress, 
"http://10-1-15-16.namespace1.pod:80";)
+}
+
+func Test_resolvePodUriIPAddressMode(t *testing.T) {
+       pod := mockPodWithContainers(namespace1, pod1,
+               *mockContainerWithPorts(container1, 
mockContainerPort(httpProtocolName, tcp, defaultHttp)),
+               *mockContainerWithPorts(container2, 
mockContainerPort(httpsProtocolName, tcp, defaultHttps)))
+       pod.Status.PodIP = "10.1.15.17"
+       doTestResolvePodUri(t, pod, "", "", KubernetesIPAddress, 
"http://10.1.15.17:80";)
+}
+
+func Test_resolvePodUriByCustomContainerDNSMode(t *testing.T) {
+       pod := mockPodWithContainers(namespace1, pod1,
+               *mockContainerWithPorts(container1, 
mockContainerPort(httpsProtocolName, tcp, defaultHttps)),
+               *mockContainerWithPorts("custom-container", 
mockContainerPort(httpProtocolName, tcp, defaultHttp)))
+       pod.Status.PodIP = "10.1.15.16"
+       doTestResolvePodUri(t, pod, "custom-container", "", 
KubernetesDNSAddress, "http://10-1-15-16.namespace1.pod:80";)
+}
+
+func Test_resolvePodUriByCustomContainerIPAddressMode(t *testing.T) {
+       pod := mockPodWithContainers(namespace1, pod1,
+               *mockContainerWithPorts(container1, 
mockContainerPort(httpsProtocolName, tcp, defaultHttps)),
+               *mockContainerWithPorts("custom-container", 
mockContainerPort(httpProtocolName, tcp, defaultHttp)))
+       pod.Status.PodIP = "10.1.15.17"
+       doTestResolvePodUri(t, pod, "custom-container", "", 
KubernetesIPAddress, "http://10.1.15.17:80";)
+}
+
+func Test_resolvePodUriByCustomContainerAndCustomPortDNSMode(t *testing.T) {
+       pod := mockPodWithContainers(namespace1, pod1,
+               *mockContainerWithPorts(container1, 
mockContainerPort(httpsProtocolName, tcp, defaultHttps)),
+               *mockContainerWithPorts("custom-container",
+                       mockContainerPort("not-wanted", tcp, 8008),
+                       mockContainerPort("custom-port", tcp, 8181)))
+       pod.Status.PodIP = "10.1.15.16"
+       doTestResolvePodUri(t, pod, "custom-container", "custom-port", 
KubernetesDNSAddress, "http://10-1-15-16.namespace1.pod:8181";)
+}
+
+func Test_resolvePodUriByCustomContainerAndCustomPortIPAddressMode(t 
*testing.T) {
+       pod := mockPodWithContainers(namespace1, pod1,
+               *mockContainerWithPorts(container1, 
mockContainerPort(httpsProtocolName, tcp, defaultHttps)),
+               *mockContainerWithPorts("custom-container",
+                       mockContainerPort("not-wanted", tcp, 8008),
+                       mockContainerPort("custom-port", tcp, 8181)))
+       pod.Status.PodIP = "10.1.15.17"
+       doTestResolvePodUri(t, pod, "custom-container", "custom-port", 
KubernetesIPAddress, "http://10.1.15.17:8181";)
+}
+
+func doTestResolvePodUri(t *testing.T, pod *corev1.Pod, customContainer 
string, customPort, outputMode string, expectedUri string) {
+       result, err := resolvePodUri(pod, customContainer, customPort, 
outputMode)
+       assert.NoError(t, err)
+       assert.Equal(t, expectedUri, result)
+}
+
+func Test_buildURI(t *testing.T) {
+       assert.Equal(t, "http://10.1.15.16:8383";, buildURI("http", 
"10.1.15.16", 8383))
+}
+
+func Test_buildKubernetesServiceDNSUri(t *testing.T) {
+       assert.Equal(t, "http://service1.namespace1.svc:8383";, 
buildKubernetesServiceDNSUri("http", namespace1, service1, 8383))
+}
+
+func Test_buildKubernetesPodDNSUri(t *testing.T) {
+       assert.Equal(t, "http://pod1.namespace1.pod:8484";, 
buildKubernetesPodDNSUri("http", namespace1, pod1, 8484))
+}
diff --git a/controllers/profiles/common/app_properties.go 
b/controllers/profiles/common/app_properties.go
index 5b2bbd12..f97e9934 100644
--- a/controllers/profiles/common/app_properties.go
+++ b/controllers/profiles/common/app_properties.go
@@ -15,9 +15,16 @@
 package common
 
 import (
+       "context"
        "fmt"
 
+       "regexp"
+       "strings"
+
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/discovery"
+
        "github.com/magiconair/properties"
+
        "k8s.io/klog/v2"
 
        operatorapi 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
@@ -38,6 +45,9 @@ const (
 
        PersistenceTypeEphemeral   = "ephemeral"
        PersistenceTypePostgressql = "postgresql"
+
+       microprofileServiceCatalogPropertyPrefix = 
"org.kie.kogito.addons.discovery."
+       discoveryLikePropertyPattern             = 
"^\\${(kubernetes|knative|openshift):(.*)}$"
 )
 
 var immutableApplicationProperties = "quarkus.http.port=" + 
DefaultHTTPWorkflowPortIntStr.String() + "\n" +
@@ -48,16 +58,21 @@ var immutableApplicationProperties = "quarkus.http.port=" + 
DefaultHTTPWorkflowP
        "quarkus.devservices.enabled=false\n" +
        "quarkus.kogito.devservices.enabled=false\n"
 
+var discoveryLikePropertyExpr = 
regexp.MustCompile(discoveryLikePropertyPattern)
+
 var _ AppPropertyHandler = &appPropertyHandler{}
 
 type AppPropertyHandler interface {
        WithUserProperties(userProperties string) AppPropertyHandler
+       WithServiceDiscovery(ctx context.Context, catalog 
discovery.ServiceCatalog) AppPropertyHandler
        Build() string
 }
 
 type appPropertyHandler struct {
        workflow                 *operatorapi.SonataFlow
        platform                 *operatorapi.SonataFlowPlatform
+       catalog                  discovery.ServiceCatalog
+       ctx                      context.Context
        userProperties           string
        defaultMutableProperties string
        isService                bool
@@ -68,6 +83,12 @@ func (a *appPropertyHandler) WithUserProperties(properties 
string) AppPropertyHa
        return a
 }
 
+func (a *appPropertyHandler) WithServiceDiscovery(ctx context.Context, catalog 
discovery.ServiceCatalog) AppPropertyHandler {
+       a.ctx = ctx
+       a.catalog = catalog
+       return a
+}
+
 func (a *appPropertyHandler) Build() string {
        var props *properties.Properties
        var propErr error = nil
@@ -88,6 +109,16 @@ func (a *appPropertyHandler) Build() string {
        // Disable expansions since it's not our responsibility
        // Property expansion means resolving ${} within the properties and 
environment context. Quarkus will do that in runtime.
        props.DisableExpansion = true
+
+       removeDiscoveryProperties(props)
+       if a.requireServiceDiscovery() {
+               // produce the MicroProfileConfigServiceCatalog properties for 
the service discovery property values if any.
+               discoveryProperties := generateDiscoveryProperties(a.ctx, 
a.catalog, props, a.workflow)
+               if discoveryProperties.Len() > 0 {
+                       props.Merge(discoveryProperties)
+               }
+       }
+
        defaultMutableProps := 
properties.MustLoadString(a.defaultMutableProperties)
        for _, k := range defaultMutableProps.Keys() {
                if _, ok := props.Get(k); ok {
@@ -183,3 +214,73 @@ func GetDataIndexName(platform 
*operatorapi.SonataFlowPlatform) string {
 func GetDataIndexCmName(platform *operatorapi.SonataFlowPlatform) string {
        return GetDataIndexName(platform) + "-props"
 }
+
+func (a *appPropertyHandler) requireServiceDiscovery() bool {
+       return a.ctx != nil && a.catalog != nil
+}
+
+// generateDiscoveryProperties Given a user configured properties set, 
generates the MicroProfileConfigServiceCatalog
+// required properties to resolve the corresponding service addresses base on 
these properties.
+// e.g.
+// Given a user configured property like this:
+//
+//     
quarkus.rest-client.acme_financial_service_yml.url=${kubernetes:services.v1/usecase1/financial-service?port=http-port}
+//
+// generates the following property:
+//
+//     
org.kie.kogito.addons.discovery.kubernetes\:services.v1\/usecase1\/financial-service?port\=http-port=http://10.5.9.1:8080
+//
+// where http://10.5.9.1:8080 is the corresponding k8s cloud address for the 
service financial-service in the namespace usecase1.
+func generateDiscoveryProperties(ctx context.Context, catalog 
discovery.ServiceCatalog, props *properties.Properties,
+       workflow *operatorapi.SonataFlow) *properties.Properties {
+       klog.V(log.I).Infof("Generating service discovery properties for 
workflow: %s, and namespace: %s.", workflow.Name, workflow.Namespace)
+       result := properties.NewProperties()
+       props.DisableExpansion = true
+       for _, k := range props.Keys() {
+               value, _ := props.Get(k)
+               klog.V(log.I).Infof("Scanning property %s=%s for service 
discovery configuration.", k, value)
+               if !discoveryLikePropertyExpr.MatchString(value) {
+                       klog.V(log.I).Infof("Skipping property %s=%s since it 
does not look like a service discovery configuration.", k, value)
+               } else {
+                       klog.V(log.I).Infof("Property %s=%s looks like a 
service discovery configuration.", k, value)
+                       plainUri := value[2 : len(value)-1]
+                       if uri, err := discovery.ParseUri(plainUri); err != nil 
{
+                               klog.V(log.I).Infof("Property %s=%s not 
correspond to a valid service discovery configuration, it will be excluded from 
service discovery.", k, value)
+                       } else {
+                               if len(uri.Namespace) == 0 {
+                                       klog.V(log.I).Infof("Current service 
discovery configuration has no configured namespace, workflow namespace: %s 
will be used instead.", workflow.Namespace)
+                                       uri.Namespace = workflow.Namespace
+                               }
+                               if address, err := catalog.Query(ctx, *uri, 
discovery.KubernetesDNSAddress); err != nil {
+                                       klog.V(log.E).ErrorS(err, "An error was 
produced during service address resolution.", "serviceUri", plainUri)
+                               } else {
+                                       klog.V(log.I).Infof("Service: %s was 
resolved into the following address: %s.", plainUri, address)
+                                       mpProperty := 
generateMicroprofileServiceCatalogProperty(plainUri)
+                                       klog.V(log.I).Infof("Generating 
microprofile service catalog property %s=%s.", mpProperty, address)
+                                       result.MustSet(mpProperty, address)
+                               }
+                       }
+               }
+       }
+       return result
+}
+
+func removeDiscoveryProperties(props *properties.Properties) {
+       for _, k := range props.Keys() {
+               if strings.HasPrefix(k, 
microprofileServiceCatalogPropertyPrefix) {
+                       props.Delete(k)
+               }
+       }
+}
+
+func generateMicroprofileServiceCatalogProperty(serviceUri string) string {
+       escapedServiceUri := escapeValue(serviceUri, ":")
+       escapedServiceUri = escapeValue(escapedServiceUri, "/")
+       escapedServiceUri = escapeValue(escapedServiceUri, "=")
+       property := microprofileServiceCatalogPropertyPrefix + escapedServiceUri
+       return property
+}
+
+func escapeValue(unescaped string, value string) string {
+       return strings.Replace(unescaped, value, fmt.Sprintf("\\%s", value), -1)
+}
diff --git a/controllers/profiles/common/app_properties_test.go 
b/controllers/profiles/common/app_properties_test.go
index 918c782f..f3d46f07 100644
--- a/controllers/profiles/common/app_properties_test.go
+++ b/controllers/profiles/common/app_properties_test.go
@@ -15,8 +15,15 @@
 package common
 
 import (
+       "context"
+       "fmt"
        "testing"
 
+       operatorapi 
"github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/discovery"
+
        "github.com/magiconair/properties"
 
        "github.com/stretchr/testify/assert"
@@ -26,6 +33,33 @@ import (
        "github.com/apache/incubator-kie-kogito-serverless-operator/test"
 )
 
+const (
+       defaultNamespace  = "default-namespace"
+       namespace1        = "namespace1"
+       myService1        = "my-service1"
+       myService1Address = "http://10.110.90.1:80";
+       myService2        = "my-service2"
+       myService2Address = "http://10.110.90.2:80";
+       myService3        = "my-service3"
+       myService3Address = "http://10.110.90.3:80";
+)
+
+type mockCatalogService struct {
+}
+
+func (c *mockCatalogService) Query(ctx context.Context, uri 
discovery.ResourceUri, outputFormat string) (string, error) {
+       if uri.Scheme == discovery.KubernetesScheme && uri.Namespace == 
namespace1 && uri.Name == myService1 {
+               return myService1Address, nil
+       }
+       if uri.Scheme == discovery.KubernetesScheme && uri.Name == myService2 
&& uri.Namespace == defaultNamespace {
+               return myService2Address, nil
+       }
+       if uri.Scheme == discovery.KubernetesScheme && uri.Name == myService3 
&& uri.Namespace == defaultNamespace && uri.GetPort() == "http-port" {
+               return myService3Address, nil
+       }
+       return "", nil
+}
+
 func Test_appPropertyHandler_WithKogitoServiceUrl(t *testing.T) {
        workflow := test.GetBaseSonataFlow("default")
        props := ImmutableApplicationProperties(workflow, nil)
@@ -110,3 +144,92 @@ func 
Test_appPropertyHandler_WithServicesWithUserOverrides(t *testing.T) {
        //quarkus.http.port remains with the default value since it's immutable.
        assert.Equal(t, "8080", generatedProps.GetString("quarkus.http.port", 
""))
 }
+
+func Test_appPropertyHandler_WithUserPropertiesWithServiceDiscovery(t 
*testing.T) {
+       //just add some user provided properties, no overrides.
+       userProperties := "property1=value1\nproperty2=value2\n"
+       //add some user properties that requires service discovery
+       userProperties = userProperties + 
"service1=${kubernetes:services.v1/namespace1/my-service1}\n"
+       userProperties = userProperties + 
"service2=${kubernetes:services.v1/my-service2}\n"
+
+       workflow := test.GetBaseSonataFlow(defaultNamespace)
+       props := NewAppPropertyHandler(workflow, nil).
+               WithUserProperties(userProperties).
+               WithServiceDiscovery(context.TODO(), &mockCatalogService{}).
+               Build()
+       generatedProps, propsErr := properties.LoadString(props)
+       generatedProps.DisableExpansion = true
+       assert.NoError(t, propsErr)
+       assert.Equal(t, 12, len(generatedProps.Keys()))
+       assertHasProperty(t, generatedProps, "property1", "value1")
+       assertHasProperty(t, generatedProps, "property2", "value2")
+
+       assertHasProperty(t, generatedProps, "service1", 
"${kubernetes:services.v1/namespace1/my-service1}")
+       assertHasProperty(t, generatedProps, "service2", 
"${kubernetes:services.v1/my-service2}")
+       
//org.kie.kogito.addons.discovery.kubernetes\:services.v1\/usecase1ยบ/my-service1
 below we use the unescaped vale because the properties.LoadString removes them.
+       assertHasProperty(t, generatedProps, 
"org.kie.kogito.addons.discovery.kubernetes:services.v1/namespace1/my-service1",
 myService1Address)
+       //org.kie.kogito.addons.discovery.kubernetes\:services.v1\/my-service2 
below we use the unescaped vale because the properties.LoadString removes them.
+       assertHasProperty(t, generatedProps, 
"org.kie.kogito.addons.discovery.kubernetes:services.v1/my-service2", 
myService2Address)
+
+       assertHasProperty(t, generatedProps, "kogito.service.url", 
fmt.Sprintf("http://greeting.%s";, defaultNamespace))
+       assertHasProperty(t, generatedProps, "quarkus.http.port", "8080")
+       assertHasProperty(t, generatedProps, "quarkus.http.host", "0.0.0.0")
+       assertHasProperty(t, generatedProps, 
"org.kie.kogito.addons.knative.eventing.health-enabled", "false")
+       assertHasProperty(t, generatedProps, "quarkus.devservices.enabled", 
"false")
+       assertHasProperty(t, generatedProps, 
"quarkus.kogito.devservices.enabled", "false")
+}
+
+func Test_generateDiscoveryProperties(t *testing.T) {
+
+       catalogService := &mockCatalogService{}
+
+       propertiesContent := "property1=value1\n"
+       propertiesContent = propertiesContent + "property2=${value2}\n"
+       propertiesContent = propertiesContent + 
"service1=${kubernetes:services.v1/namespace1/my-service1}\n"
+       propertiesContent = propertiesContent + 
"service2=${kubernetes:services.v1/my-service2}\n"
+       propertiesContent = propertiesContent + 
"service3=${kubernetes:services.v1/my-service3?port=http-port}\n"
+
+       propertiesContent = propertiesContent + 
"non_service4=${kubernetes:--kaka}"
+
+       props := properties.MustLoadString(propertiesContent)
+       result := generateDiscoveryProperties(context.TODO(), catalogService, 
props, &operatorapi.SonataFlow{
+               ObjectMeta: metav1.ObjectMeta{Name: "helloworld", Namespace: 
defaultNamespace},
+       })
+
+       assert.Equal(t, result.Len(), 3)
+       assertHasProperty(t, result, 
"org.kie.kogito.addons.discovery.kubernetes\\:services.v1\\/namespace1\\/my-service1",
 myService1Address)
+       assertHasProperty(t, result, 
"org.kie.kogito.addons.discovery.kubernetes\\:services.v1\\/my-service2", 
myService2Address)
+       assertHasProperty(t, result, 
"org.kie.kogito.addons.discovery.kubernetes\\:services.v1\\/my-service3?port\\=http-port",
 myService3Address)
+}
+
+func assertHasProperty(t *testing.T, props *properties.Properties, 
expectedProperty string, expectedValue string) {
+       value, ok := props.Get(expectedProperty)
+       assert.True(t, ok, "Property %s, is not present as expected.", 
expectedProperty)
+       assert.Equal(t, expectedValue, value, "Expected value for property: %s, 
is: %s but current value is: %s", expectedProperty, expectedValue, value)
+}
+
+func Test_generateMicroprofileServiceCatalogProperty(t *testing.T) {
+
+       doTestGenerateMicroprofileServiceCatalogProperty(t, 
"kubernetes:services.v1/namespace1/financial-service",
+               
"org.kie.kogito.addons.discovery.kubernetes\\:services.v1\\/namespace1\\/financial-service")
+
+       doTestGenerateMicroprofileServiceCatalogProperty(t, 
"kubernetes:services.v1/financial-service",
+               
"org.kie.kogito.addons.discovery.kubernetes\\:services.v1\\/financial-service")
+
+       doTestGenerateMicroprofileServiceCatalogProperty(t, 
"kubernetes:pods.v1/namespace1/financial-service",
+               
"org.kie.kogito.addons.discovery.kubernetes\\:pods.v1\\/namespace1\\/financial-service")
+
+       doTestGenerateMicroprofileServiceCatalogProperty(t, 
"kubernetes:pods.v1/financial-service",
+               
"org.kie.kogito.addons.discovery.kubernetes\\:pods.v1\\/financial-service")
+
+       doTestGenerateMicroprofileServiceCatalogProperty(t, 
"kubernetes:deployments.v1.apps/namespace1/financial-service",
+               
"org.kie.kogito.addons.discovery.kubernetes\\:deployments.v1.apps\\/namespace1\\/financial-service")
+
+       doTestGenerateMicroprofileServiceCatalogProperty(t, 
"kubernetes:deployments.v1.apps/financial-service",
+               
"org.kie.kogito.addons.discovery.kubernetes\\:deployments.v1.apps\\/financial-service")
+}
+
+func doTestGenerateMicroprofileServiceCatalogProperty(t *testing.T, serviceUri 
string, expectedProperty string) {
+       mpProperty := generateMicroprofileServiceCatalogProperty(serviceUri)
+       assert.Equal(t, mpProperty, expectedProperty, "expected microprofile 
service catalog property for serviceUri: %s, is %s, but the returned value was: 
%s", serviceUri, expectedProperty, mpProperty)
+}
diff --git a/controllers/profiles/common/mutate_visitors.go 
b/controllers/profiles/common/mutate_visitors.go
index 7c52a5ae..2e2a4ece 100644
--- a/controllers/profiles/common/mutate_visitors.go
+++ b/controllers/profiles/common/mutate_visitors.go
@@ -15,6 +15,9 @@
 package common
 
 import (
+       "context"
+
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/discovery"
        "github.com/imdario/mergo"
        appsv1 "k8s.io/api/apps/v1"
        corev1 "k8s.io/api/core/v1"
@@ -94,7 +97,8 @@ func ServiceMutateVisitor(workflow *operatorapi.SonataFlow) 
MutateVisitor {
        }
 }
 
-func WorkflowPropertiesMutateVisitor(workflow *operatorapi.SonataFlow, 
platform *operatorapi.SonataFlowPlatform) MutateVisitor {
+func WorkflowPropertiesMutateVisitor(ctx context.Context, catalog 
discovery.ServiceCatalog,
+       workflow *operatorapi.SonataFlow, platform 
*operatorapi.SonataFlowPlatform) MutateVisitor {
        return func(object client.Object) controllerutil.MutateFn {
                return func() error {
                        if kubeutil.IsObjectNew(object) {
@@ -113,6 +117,7 @@ func WorkflowPropertiesMutateVisitor(workflow 
*operatorapi.SonataFlow, platform
                        cm.Data[workflowproj.ApplicationPropertiesFileName] =
                                NewAppPropertyHandler(workflow, platform).
                                        
WithUserProperties(cm.Data[workflowproj.ApplicationPropertiesFileName]).
+                                       WithServiceDiscovery(ctx, catalog).
                                        Build()
 
                        return nil
diff --git a/controllers/profiles/common/object_creators_test.go 
b/controllers/profiles/common/object_creators_test.go
index 215890d1..4b5f74be 100644
--- a/controllers/profiles/common/object_creators_test.go
+++ b/controllers/profiles/common/object_creators_test.go
@@ -39,7 +39,7 @@ func Test_ensureWorkflowPropertiesConfigMapMutator(t 
*testing.T) {
        cm.SetResourceVersion("1")
        reflectCm := cm.(*corev1.ConfigMap)
 
-       visitor := WorkflowPropertiesMutateVisitor(workflow, nil)
+       visitor := WorkflowPropertiesMutateVisitor(nil, nil, workflow, nil)
        mutateFn := visitor(cm)
 
        assert.NoError(t, mutateFn())
@@ -72,7 +72,7 @@ func 
Test_ensureWorkflowPropertiesConfigMapMutator_DollarReplacement(t *testing.
                        workflowproj.ApplicationPropertiesFileName: 
"mp.messaging.outgoing.kogito_outgoing_stream.url=${kubernetes:services.v1/event-listener}",
                },
        }
-       mutateVisitorFn := WorkflowPropertiesMutateVisitor(workflow, nil)
+       mutateVisitorFn := WorkflowPropertiesMutateVisitor(nil, nil, workflow, 
nil)
 
        err := mutateVisitorFn(existingCM)()
        assert.NoError(t, err)
diff --git a/controllers/profiles/common/reconciler.go 
b/controllers/profiles/common/reconciler.go
index 3b68e769..4eda42e4 100644
--- a/controllers/profiles/common/reconciler.go
+++ b/controllers/profiles/common/reconciler.go
@@ -18,6 +18,8 @@ import (
        "context"
        "fmt"
 
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/discovery"
+
        "k8s.io/klog/v2"
        ctrl "sigs.k8s.io/controller-runtime"
        "sigs.k8s.io/controller-runtime/pkg/client"
@@ -29,7 +31,8 @@ import (
 
 // StateSupport is the shared structure with common accessors used throughout 
the whole reconciliation profiles
 type StateSupport struct {
-       C client.Client
+       C       client.Client
+       Catalog discovery.ServiceCatalog
 }
 
 // PerformStatusUpdate updates the SonataFlow Status conditions
diff --git a/controllers/profiles/dev/states_dev.go 
b/controllers/profiles/dev/states_dev.go
index f22cf581..6e5296af 100644
--- a/controllers/profiles/dev/states_dev.go
+++ b/controllers/profiles/dev/states_dev.go
@@ -68,7 +68,7 @@ func (e *ensureRunningWorkflowState) Do(ctx context.Context, 
workflow *operatora
        if err == nil && len(pl.Spec.DevMode.BaseImage) > 0 {
                devBaseContainerImage = pl.Spec.DevMode.BaseImage
        }
-       propsCM, _, err := e.ensurers.propertiesConfigMap.Ensure(ctx, workflow, 
common.WorkflowPropertiesMutateVisitor(workflow, pl))
+       propsCM, _, err := e.ensurers.propertiesConfigMap.Ensure(ctx, workflow, 
common.WorkflowPropertiesMutateVisitor(ctx, e.StateSupport.Catalog, workflow, 
pl))
        if err != nil {
                return ctrl.Result{Requeue: false}, objs, err
        }
diff --git a/controllers/profiles/prod/deployment_handler.go 
b/controllers/profiles/prod/deployment_handler.go
index 4d950ee5..d00f427f 100644
--- a/controllers/profiles/prod/deployment_handler.go
+++ b/controllers/profiles/prod/deployment_handler.go
@@ -48,7 +48,7 @@ func (d *deploymentHandler) handle(ctx context.Context, 
workflow *operatorapi.So
 
 func (d *deploymentHandler) handleWithImage(ctx context.Context, workflow 
*operatorapi.SonataFlow, image string) (reconcile.Result, []client.Object, 
error) {
        pl, _ := platform.GetActivePlatform(ctx, d.C, workflow.Namespace)
-       propsCM, _, err := d.ensurers.propertiesConfigMap.Ensure(ctx, workflow, 
common.WorkflowPropertiesMutateVisitor(workflow, pl))
+       propsCM, _, err := d.ensurers.propertiesConfigMap.Ensure(ctx, workflow, 
common.WorkflowPropertiesMutateVisitor(ctx, d.StateSupport.Catalog, workflow, 
pl))
        if err != nil {
                workflow.Status.Manager().MarkFalse(api.RunningConditionType, 
api.ExternalResourcesNotFoundReason, "Unable to retrieve the properties config 
map")
                _, err = d.PerformStatusUpdate(ctx, workflow)
diff --git a/controllers/profiles/prod/profile_prod.go 
b/controllers/profiles/prod/profile_prod.go
index 03f98649..1529f0ef 100644
--- a/controllers/profiles/prod/profile_prod.go
+++ b/controllers/profiles/prod/profile_prod.go
@@ -15,8 +15,11 @@
 package prod
 
 import (
+       "fmt"
        "time"
 
+       
"github.com/apache/incubator-kie-kogito-serverless-operator/controllers/discovery"
+
        "sigs.k8s.io/controller-runtime/pkg/client"
 
        
"github.com/apache/incubator-kie-kogito-serverless-operator/api/metadata"
@@ -59,8 +62,10 @@ func newObjectEnsurers(support *common.StateSupport) 
*objectEnsurers {
 // to have an immutable workflow image deployed
 func NewProfileReconciler(client client.Client) profiles.ProfileReconciler {
        support := &common.StateSupport{
-               C: client,
+               C:       client,
+               Catalog: discovery.NewServiceCatalog(client),
        }
+       fmt.Println(fmt.Sprintf("XXX Prod NewProfileReconciler"))
        // the reconciliation state machine
        stateMachine := common.NewReconciliationStateMachine(
                &newBuilderState{StateSupport: support},
@@ -78,7 +83,8 @@ func NewProfileReconciler(client client.Client) 
profiles.ProfileReconciler {
 // the workflow application. It assumes that the image has been built 
somewhere else.
 func NewProfileForOpsReconciler(client client.Client) 
profiles.ProfileReconciler {
        support := &common.StateSupport{
-               C: client,
+               C:       client,
+               Catalog: discovery.NewServiceCatalog(client),
        }
        // the reconciliation state machine
        stateMachine := common.NewReconciliationStateMachine(
diff --git a/go.work.sum b/go.work.sum
index c4000573..96676560 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -177,7 +177,6 @@ github.com/buger/jsonparser v1.1.1 
h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMU
 github.com/cenkalti/backoff/v4 v4.1.1/go.mod 
h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
 github.com/cenkalti/backoff/v4 v4.1.3 
h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
 github.com/cenkalti/backoff/v4 v4.1.3/go.mod 
h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
-github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod 
h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
 github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod 
h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
 github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod 
h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
 github.com/cespare/xxhash v1.1.0 
h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
@@ -379,7 +378,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 
h1:Ovs26xHkKqVztRpIrF/92Bcuy
 github.com/grpc-ecosystem/grpc-gateway v1.16.0 
h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 
h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod 
h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod 
h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
 github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce 
h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/iancoleman/strcase v0.2.0/go.mod 
h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
@@ -568,7 +566,6 @@ go.mongodb.org/mongo-driver v1.3.0/go.mod 
h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS
 go.mongodb.org/mongo-driver v1.3.4/go.mod 
h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
 go.mongodb.org/mongo-driver v1.5.1 
h1:9nOVLGDfOaZ9R0tBumx/BcuqkbFpyTCU2r/Po7A2azI=
 go.mongodb.org/mongo-driver v1.5.1/go.mod 
h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=
-go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 
v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 
v0.35.0 h1:xFSRQBbXF6VvYRf2lqMJXxoB72XI1K/azav8TekHHSw=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 
v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY=
@@ -732,7 +729,6 @@ google.golang.org/api v0.57.0/go.mod 
h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdr
 google.golang.org/api v0.58.0/go.mod 
h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E=
 google.golang.org/api v0.61.0 h1:TXXKS1slM3b2bZNJwD5DV/Tp6/M2cLzLOLh9PjDhrw8=
 google.golang.org/api v0.61.0/go.mod 
h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.114.0/go.mod 
h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod 
h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod 
h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod 
h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@@ -766,7 +762,6 @@ google.golang.org/genproto 
v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP
 google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod 
h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
 google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod 
h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
 google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod 
h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod 
h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
 google.golang.org/grpc v1.34.0/go.mod 
h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
 google.golang.org/grpc v1.35.0/go.mod 
h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.37.0/go.mod 
h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
@@ -781,7 +776,6 @@ google.golang.org/grpc v1.46.2/go.mod 
h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu
 google.golang.org/grpc v1.49.0/go.mod 
h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 google.golang.org/grpc v1.53.0/go.mod 
h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
 google.golang.org/grpc v1.54.0/go.mod 
h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
-google.golang.org/grpc v1.56.3/go.mod 
h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod 
h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v1.28.0/go.mod 
h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.28.1/go.mod 
h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
diff --git a/testbdd/go.sum b/testbdd/go.sum
index 21b46ce5..8b846334 100644
--- a/testbdd/go.sum
+++ b/testbdd/go.sum
@@ -78,6 +78,7 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod 
h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod 
h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod 
h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/RHsyseng/operator-utils v1.4.13 
h1:kCsvBXm1Y3AEfzjioUvk/RmOigM/+czd/U5YQ3SZXx8=
+github.com/RHsyseng/operator-utils v1.4.13/go.mod 
h1:f+GrcLNALoHBPonk3P6KCwPK5kYyHhkqj4vuCP2Eijc=
 github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod 
h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
 github.com/Shopify/sarama v1.19.0/go.mod 
h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod 
h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
@@ -137,6 +138,7 @@ github.com/c2h5oh/datasize 
v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9cop
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod 
h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod 
h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/census-instrumentation/opencensus-proto v0.4.1 
h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
+github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod 
h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
 github.com/cespare/xxhash v1.1.0/go.mod 
h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.0/go.mod 
h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod 
h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -522,6 +524,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod 
h1:vNeuVxBJEsws4ogUvrchl83t
 github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod 
h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod 
h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 
h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod 
h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
 github.com/grpc-ecosystem/grpc-health-probe 
v0.2.1-0.20181220223928-2bf0a5b182db/go.mod 
h1:uBKkC2RbarFsvS5jMJHpVhTLvGlGQj9JJwkaePE3FWI=
 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod 
h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
 github.com/hashicorp/consul/api v1.1.0/go.mod 
h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
@@ -822,6 +825,7 @@ github.com/prometheus/statsd_exporter v0.21.0/go.mod 
h1:rbT83sZq2V+p73lHhPZfMc3M
 github.com/prometheus/tsdb v0.7.1/go.mod 
h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod 
h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/relvacode/iso8601 v1.3.0 
h1:HguUjsGpIMh/zsTczGN3DVJFxTU/GX+MMmzcKoMO7ko=
+github.com/relvacode/iso8601 v1.3.0/go.mod 
h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
 github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod 
h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
 github.com/rickb777/date v1.13.0 
h1:+8AmwLuY1d/rldzdqvqTEg7107bZ8clW37x4nsdG3Hs=
 github.com/rickb777/date v1.13.0/go.mod 
h1:GZf3LoGnxPWjX+/1TXOuzHefZFDovTyNLHDMd3qH70k=
@@ -847,6 +851,7 @@ github.com/sergi/go-diff v1.0.0/go.mod 
h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/sergi/go-diff v1.1.0/go.mod 
h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 github.com/serverlessworkflow/sdk-go/v2 v2.2.5 
h1:/TFqBBni0hDpTA0bKadGTWbyBRiQ0o2ppz2ScY6DdTM=
+github.com/serverlessworkflow/sdk-go/v2 v2.2.5/go.mod 
h1:uIy7EgNRGUzuTsihdto7fN+xsz/HDHq0MP1aPIG7wHU=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod 
h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod 
h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod 
h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
@@ -904,6 +909,7 @@ github.com/stretchr/testify v1.7.1/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.8.0/go.mod 
h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod 
h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.8.3 
h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
+github.com/stretchr/testify v1.8.3/go.mod 
h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/subosito/gotenv v1.2.0/go.mod 
h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod 
h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
 github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod 
h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@@ -960,6 +966,7 @@ go.opencensus.io v0.22.4/go.mod 
h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
+go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 go.opentelemetry.io/otel v0.16.0/go.mod 
h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod 
h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod 
h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -1010,6 +1017,7 @@ golang.org/x/crypto 
v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod 
h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod 
h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod 
h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1106,6 +1114,7 @@ golang.org/x/net 
v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod 
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod 
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod 
h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod 
h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1211,10 +1220,12 @@ golang.org/x/sys 
v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod 
h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod 
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod 
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
+golang.org/x/term v0.13.0/go.mod 
h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
 golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod 
h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod 
h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1226,6 +1237,7 @@ golang.org/x/text v0.3.5/go.mod 
h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
+golang.org/x/text v0.13.0/go.mod 
h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1340,6 +1352,7 @@ google.golang.org/api v0.30.0/go.mod 
h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
 google.golang.org/api v0.35.0/go.mod 
h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
 google.golang.org/api v0.36.0/go.mod 
h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
 google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
+google.golang.org/api v0.114.0/go.mod 
h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
 google.golang.org/appengine v1.1.0/go.mod 
h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.3.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.4.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1389,6 +1402,7 @@ google.golang.org/genproto 
v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20210416161957-9910b6c460de/go.mod 
h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
 google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod 
h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 
h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
+google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod 
h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
 google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod 
h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.17.0/go.mod 
h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod 
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1413,6 +1427,7 @@ google.golang.org/grpc v1.36.0/go.mod 
h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
 google.golang.org/grpc v1.36.1/go.mod 
h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.40.0/go.mod 
h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
+google.golang.org/grpc v1.56.3/go.mod 
h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod 
h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod 
h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod 
h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/utils/kubernetes/deployment.go b/utils/kubernetes/deployment.go
index 4ddf4e17..8ede5bb2 100644
--- a/utils/kubernetes/deployment.go
+++ b/utils/kubernetes/deployment.go
@@ -111,6 +111,21 @@ func GetContainerByName(name string, podSpec *v1.PodSpec) 
(*v1.Container, int) {
        return nil, -1
 }
 
+// GetContainerPortByName returns a pointer to the ContainerPort within the 
given Container.
+// If none found, returns nil.
+// It also returns the position where the container port was found, -1 if none.
+func GetContainerPortByName(name string, container *v1.Container) 
(*v1.ContainerPort, int) {
+       if container == nil {
+               return nil, -1
+       }
+       for i, containerPort := range container.Ports {
+               if name == containerPort.Name {
+                       return &containerPort, i
+               }
+       }
+       return nil, -1
+}
+
 // AddOrReplaceContainer replace the existing container or add if it doesn't 
exist in the .spec.containers attribute
 func AddOrReplaceContainer(containerName string, container v1.Container, 
podSpec *v1.PodSpec) {
        _, idx := GetContainerByName(containerName, podSpec)
diff --git a/utils/kubernetes/service.go b/utils/kubernetes/service.go
index 7acd6489..f77984ab 100644
--- a/utils/kubernetes/service.go
+++ b/utils/kubernetes/service.go
@@ -42,3 +42,18 @@ func RetrieveServiceURL(service *v1.Service) (*apis.URL, 
error) {
                Path:   service.Name}
        return apis.ParseURL(url.String())
 }
+
+// GetServicePortByName returns a pointer to the ServicePort within the given 
Service.
+// If none found, returns nil.
+// It also returns the position where the service port por was found, -1 if 
none.
+func GetServicePortByName(name string, service *v1.Service) (*v1.ServicePort, 
int) {
+       if service == nil {
+               return nil, -1
+       }
+       for i, servicePort := range service.Spec.Ports {
+               if name == servicePort.Name {
+                       return &servicePort, i
+               }
+       }
+       return nil, -1
+}


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

Reply via email to