Repository: redhat-developer/service-binding-operator
Pull request #428 (2020-04-18 07:55:03)

Add an envVarPrefix field to backingServiceSelector
### Motivation

According to issue https://github.com/redhat-developer/service-binding-operator/issues/387, an envVarPrefix will be added to backingServiceSelector to support multiple service binding as below shows:

  ```
backingServiceSelectors:
    - group: groupname
      version: v1alpha1
      kind: Binding
      resourceRef: resourceName
      envVarPrefix: testprefix
```

### Changes

Add a field envVarPrefix.



openshift-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **akashshinde**
You can assign the PR to them by writing `/assign @akashshinde` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=redhat-developer%2Fservice-binding-operator).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/redhat-developer/service-binding-operator/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["akashshinde"]} -->
openshift-ci-robot: Hi @qibobo. Thanks for your PR.

I'm waiting for a [redhat-developer](https://github.com/orgs/redhat-developer/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://github.com/orgs/redhat-developer/people) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=redhat-developer%2Fservice-binding-operator).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

sbose78: Thank you!

/ok-to-test
codecov[bot]: # [Codecov](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428?src=pr&el=h1) Report
> Merging [#428](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428?src=pr&el=desc) into [master](https://codecov.io/gh/redhat-developer/service-binding-operator/commit/62ab03216c2a5ac2e46ca13a816d6c0511514a28&el=desc) will **increase** coverage by `0.17%`.
> The diff coverage is `87.09%`.

[![Impacted file tree graph](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428/graphs/tree.svg?width=650&height=150&src=pr&token=CICBeVWlrC)](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #428      +/-   ##
==========================================
+ Coverage   64.59%   64.77%   +0.17%     
==========================================
  Files          21       21              
  Lines        1418     1425       +7     
==========================================
+ Hits          916      923       +7     
  Misses        376      376              
  Partials      126      126              
```


| [Impacted Files](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...troller/servicebindingrequest/related\_resources.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3JlbGF0ZWRfcmVzb3VyY2VzLmdv) | `100.00% <ø> (ø)` | |
| [...roller/servicebindingrequest/retrieve\_decoupled.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3JldHJpZXZlX2RlY291cGxlZC5nbw==) | `75.60% <60.00%> (ø)` | |
| [pkg/controller/servicebindingrequest/binding.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L2JpbmRpbmcuZ28=) | `69.12% <100.00%> (ø)` | |
| [pkg/controller/servicebindingrequest/planner.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3BsYW5uZXIuZ28=) | `94.11% <100.00%> (+0.08%)` | :arrow_up: |
| [pkg/controller/servicebindingrequest/retriever.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3JldHJpZXZlci5nbw==) | `84.41% <100.00%> (+0.63%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428?src=pr&el=footer). Last update [0ef6872...42b897c](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/428?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

qibobo: @sbose78 
Thank you for your comments. I have updated the code and added the relevant unit tests.
sbose78: What does the behaviour look like if there's a global env var prefix and there's a local env var prefix?
qibobo: @sbose78 
When global env prefix and local env prefix are both not empty, the result is GlobalEnvPrefix_LocalEnvPrefix_Key, just as https://github.com/qibobo/service-binding-operator/blob/396316955c397446a17a05a075353c95d34c3682/pkg/controller/servicebindingrequest/retriever.go#L261.
And the ut for this is https://github.com/qibobo/service-binding-operator/blob/396316955c397446a17a05a075353c95d34c3682/pkg/controller/servicebindingrequest/retriever_test.go#L126.
qibobo: rebase the master.
Avni-Sharma: Case 1- when envVarPrefix is provided
![Screenshot from 2020-04-20 21-46-27](https://user-images.githubusercontent.com/18097007/79775417-d6f21380-8351-11ea-91eb-7ce355734af2.png)

Avni-Sharma: Case 2 - when envVarPrefix is not mentioned (is omitted), then append with KIND_
![Screenshot from 2020-04-20 21-53-29](https://user-images.githubusercontent.com/18097007/79775554-0c96fc80-8352-11ea-8aba-68533352b129.png)

Avni-Sharma: Case 3- when envVarPrefix is given as an empty string `envVarPrefix: ""`

![Screenshot from 2020-04-20 21-55-25](https://user-images.githubusercontent.com/18097007/79775731-4cf67a80-8352-11ea-84d5-6218a71decd7.png)

Avni-Sharma: /lgtm
sbose78: Thank you @Avni-Sharma for the validation.

Patch:
diff --git a/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml b/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml
index 7b6a4688..e1d738ee 100644
--- a/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml
+++ b/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml
@@ -114,6 +114,8 @@ spec:
                   type: string
                 version:
                   type: string
+                envVarPrefix:
+                  type: string
               required:
               - group
               - kind
@@ -138,6 +140,8 @@ spec:
                     type: string
                   version:
                     type: string
+                  envVarPrefix:
+                    type: string
                 required:
                 - group
                 - kind
diff --git a/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go b/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go
index 7ecb280b..858f2f5c 100644
--- a/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go
+++ b/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go
@@ -66,7 +66,8 @@ type BackingServiceSelector struct {
 	metav1.GroupVersionKind `json:",inline"`
 	ResourceRef             string `json:"resourceRef"`
 	// +optional
-	Namespace *string `json:"namespace,omitempty"`
+	Namespace    *string `json:"namespace,omitempty"`
+	EnvVarPrefix *string `json:"envVarPrefix,omitempty"`
 }
 
 // BoundApplication defines the application workloads to which the binding secret has
diff --git a/pkg/controller/servicebindingrequest/binder.go b/pkg/controller/servicebindingrequest/binder.go
index 4811aafa..9840bb6f 100644
--- a/pkg/controller/servicebindingrequest/binder.go
+++ b/pkg/controller/servicebindingrequest/binder.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"strings"
 	"time"
 
 	"k8s.io/apimachinery/pkg/fields"
@@ -12,8 +13,8 @@ import (
 
 	"gotest.tools/assert/cmp"
 	corev1 "k8s.io/api/core/v1"
-	k8serror "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
@@ -22,6 +23,7 @@ import (
 
 	"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1"
 	"github.com/redhat-developer/service-binding-operator/pkg/log"
+	knativev1 "knative.dev/serving/pkg/apis/serving/v1"
 )
 
 var (
@@ -42,10 +44,27 @@ type Binder struct {
 	dynClient  dynamic.Interface               // kubernetes dynamic api client
 	sbr        *v1alpha1.ServiceBindingRequest // instantiated service binding request
 	volumeKeys []string                        // list of key names used in volume mounts
+	modifier   ExtraFieldsModifier             // extra modifier for CRDs before updating
 	logger     *log.Log                        // logger instance
 }
 
+// ExtraFieldsModifier is useful for updating backend service which requires additional changes besides
+// env/volumes updating. eg. for knative service we need to remove or update `spec.template.metadata.name`
+// from service template before updating otherwise it will be rejected.
+type ExtraFieldsModifier interface {
+	ModifyExtraFields(u *unstructured.Unstructured) error
+}
+
+// ExtraFieldsModifierFunc func receiver type for ExtraFieldsModifier
+type ExtraFieldsModifierFunc func(u *unstructured.Unstructured) error
+
+// ModifyExtraFields implements ExtraFieldsModifier interface
+func (f ExtraFieldsModifierFunc) ModifyExtraFields(u *unstructured.Unstructured) error {
+	return f(u)
+}
+
 var EmptyApplicationSelectorErr = errors.New("application ResourceRef or MatchLabel not found")
+var ApplicationNotFound = errors.New("Application is already deleted")
 
 // search objects based in Kind/APIVersion, which contain the labels defined in ApplicationSelector.
 func (b *Binder) search() (*unstructured.UnstructuredList, error) {
@@ -76,15 +95,13 @@ func (b *Binder) search() (*unstructured.UnstructuredList, error) {
 
 	objList, err := b.dynClient.Resource(gvr).Namespace(ns).List(opts)
 	if err != nil {
-		return nil, err
+		return nil, ApplicationNotFound
+
 	}
 
 	// Return fake NotFound error explicitly to ensure requeue when objList(^) is empty.
 	if len(objList.Items) == 0 {
-		return nil, k8serror.NewNotFound(
-			gvr.GroupResource(),
-			b.sbr.Spec.ApplicationSelector.GroupVersionResource.Resource,
-		)
+		return nil, ApplicationNotFound
 	}
 	return objList, err
 }
@@ -478,6 +495,13 @@ func (b *Binder) update(objs *unstructured.UnstructuredList) ([]*unstructured.Un
 			continue
 		}
 
+		if b.modifier != nil {
+			err = b.modifier.ModifyExtraFields(updatedObj)
+			if err != nil {
+				return nil, err
+			}
+		}
+
 		log.Debug("Updating object...")
 		if err := b.client.Update(b.ctx, updatedObj); err != nil {
 			return nil, err
@@ -529,6 +553,9 @@ func (b *Binder) remove(objs *unstructured.UnstructuredList) error {
 func (b *Binder) Unbind() error {
 	objs, err := b.search()
 	if err != nil {
+		if errors.Is(err, ApplicationNotFound) {
+			return nil
+		}
 		return err
 	}
 	return b.remove(objs)
@@ -539,6 +566,9 @@ func (b *Binder) Unbind() error {
 func (b *Binder) Bind() ([]*unstructured.Unstructured, error) {
 	objs, err := b.search()
 	if err != nil {
+		if errors.Is(err, ApplicationNotFound) {
+			return nil, nil
+		}
 		return nil, err
 	}
 	return b.update(objs)
@@ -552,12 +582,36 @@ func NewBinder(
 	sbr *v1alpha1.ServiceBindingRequest,
 	volumeKeys []string,
 ) *Binder {
+
+	logger := log.NewLog("binder")
+	modifier := extraFieldsModifier(logger, sbr)
+
 	return &Binder{
 		ctx:        ctx,
 		client:     client,
 		dynClient:  dynClient,
 		sbr:        sbr,
 		volumeKeys: volumeKeys,
-		logger:     log.NewLog("binder"),
+		modifier:   modifier,
+		logger:     logger,
+	}
+}
+
+func extraFieldsModifier(logger *log.Log, sbr *v1alpha1.ServiceBindingRequest) ExtraFieldsModifier {
+	gvr := sbr.Spec.ApplicationSelector.GroupVersionResource
+	ksvcgvr := knativev1.SchemeGroupVersion.WithResource("services")
+	switch gvr.String() {
+	case ksvcgvr.String():
+		pathToRevisionName := "spec.template.metadata.name"
+		return ExtraFieldsModifierFunc(func(u *unstructured.Unstructured) error {
+			revisionName, ok, err := unstructured.NestedString(u.Object, strings.Split(pathToRevisionName, ".")...)
+			if err == nil && ok {
+				logger.Info("remove revision in knative service template", "name", revisionName)
+				unstructured.RemoveNestedField(u.Object, strings.Split(pathToRevisionName, ".")...)
+			}
+			return nil
+		})
+	default:
+		return nil
 	}
 }
diff --git a/pkg/controller/servicebindingrequest/binding.go b/pkg/controller/servicebindingrequest/binding.go
index 8e35e8f2..760db507 100644
--- a/pkg/controller/servicebindingrequest/binding.go
+++ b/pkg/controller/servicebindingrequest/binding.go
@@ -328,7 +328,7 @@ func BuildServiceBinder(options *ServiceBinderOptions) (*ServiceBinder, error) {
 
 	// read bindable data from the specified resources
 	if options.DetectBindingResources {
-		err := retriever.ReadBindableResourcesData(&plan.SBR, rs)
+		err := retriever.ReadBindableResourcesData(&plan.SBR, plan.GetRelatedResources())
 		if err != nil {
 			return nil, err
 		}
@@ -336,7 +336,7 @@ func BuildServiceBinder(options *ServiceBinderOptions) (*ServiceBinder, error) {
 
 	// read bindable data from the CRDDescription found by the planner
 	for _, r := range plan.GetRelatedResources() {
-		err = retriever.ReadCRDDescriptionData(r.CR, r.CRDDescription)
+		err = retriever.ReadCRDDescriptionData(r.EnvVarPrefix, r.CR, r.CRDDescription)
 		if err != nil {
 			return nil, err
 		}
diff --git a/pkg/controller/servicebindingrequest/planner.go b/pkg/controller/servicebindingrequest/planner.go
index 44ffde51..fc5dc5a9 100644
--- a/pkg/controller/servicebindingrequest/planner.go
+++ b/pkg/controller/servicebindingrequest/planner.go
@@ -79,11 +79,6 @@ var EmptyBackingServiceSelectorsErr = errors.New("backing service selectors are
 func (p *Planner) Plan() (*Plan, error) {
 	ns := p.sbr.GetNamespace()
 
-	var emptyApplication v1alpha1.ApplicationSelector
-	if p.sbr.Spec.ApplicationSelector == emptyApplication {
-		return nil, EmptyApplicationSelectorErr
-	}
-
 	var selectors []v1alpha1.BackingServiceSelector
 	if p.sbr.Spec.BackingServiceSelector != nil {
 		selectors = append(selectors, *p.sbr.Spec.BackingServiceSelector)
@@ -149,6 +144,7 @@ func (p *Planner) Plan() (*Plan, error) {
 		r := &RelatedResource{
 			CRDDescription: crdDescription,
 			CR:             cr,
+			EnvVarPrefix:   s.EnvVarPrefix,
 		}
 		relatedResources = append(relatedResources, r)
 		p.logger.Debug("Resolved related resource", "RelatedResource", r)
diff --git a/pkg/controller/servicebindingrequest/reconciler.go b/pkg/controller/servicebindingrequest/reconciler.go
index c6f574dd..0c6df2a8 100644
--- a/pkg/controller/servicebindingrequest/reconciler.go
+++ b/pkg/controller/servicebindingrequest/reconciler.go
@@ -1,6 +1,8 @@
 package servicebindingrequest
 
 import (
+	"errors"
+
 	v1 "github.com/openshift/custom-resource-status/conditions/v1"
 	"github.com/redhat-developer/service-binding-operator/pkg/conditions"
 	corev1 "k8s.io/api/core/v1"
@@ -114,6 +116,10 @@ func (r *Reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err
 	// fetch and validate namespaced ServiceBindingRequest instance
 	sbr, err := r.getServiceBindingRequest(request.NamespacedName)
 	if err != nil {
+		if errors.Is(err, ApplicationNotFound) {
+			logger.Info("SBR deleted after application deletion")
+			return Done()
+		}
 		logger.Error(err, "On retrieving service-binding-request instance.")
 		return DoneOnNotFound(err)
 	}
@@ -147,8 +153,10 @@ func (r *Reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err
 		if err == EmptyBackingServiceSelectorsErr || err == EmptyApplicationSelectorErr {
 			// TODO: find or create an error type containing suitable information to be propagated
 			var reason string
-			if err == EmptyBackingServiceSelectorsErr {
+			if errors.Is(err, EmptyBackingServiceSelectorsErr) {
 				reason = "EmptyBackingServiceSelectors"
+			} else if errors.Is(err, ApplicationNotFound) {
+				reason = "ApplicationUnavailable"
 			} else {
 				reason = "EmptyApplicationSelector"
 			}
diff --git a/pkg/controller/servicebindingrequest/related_resources.go b/pkg/controller/servicebindingrequest/related_resources.go
index 3f0dfb72..288aba56 100644
--- a/pkg/controller/servicebindingrequest/related_resources.go
+++ b/pkg/controller/servicebindingrequest/related_resources.go
@@ -9,6 +9,7 @@ import (
 type RelatedResource struct {
 	CRDDescription *v1alpha1.CRDDescription
 	CR             *unstructured.Unstructured
+	EnvVarPrefix   *string
 }
 
 // RelatedResources contains a collection of SBR related resources.
diff --git a/pkg/controller/servicebindingrequest/retrieve_decoupled.go b/pkg/controller/servicebindingrequest/retrieve_decoupled.go
index cedc88e5..286a9ac9 100644
--- a/pkg/controller/servicebindingrequest/retrieve_decoupled.go
+++ b/pkg/controller/servicebindingrequest/retrieve_decoupled.go
@@ -36,11 +36,11 @@ func (r *Retriever) Get() (map[string][]byte, error) {
 // ReadBindableResourcesData reads all related resources of a given sbr
 func (r *Retriever) ReadBindableResourcesData(
 	sbr *v1alpha1.ServiceBindingRequest,
-	crs []*unstructured.Unstructured,
+	relatedResources RelatedResources,
 ) error {
 	r.logger.Info("Detecting extra resources for binding...")
-	for _, cr := range crs {
-		b := NewDetectBindableResources(sbr, cr, []schema.GroupVersionResource{
+	for _, rs := range ([]*RelatedResource)(relatedResources) {
+		b := NewDetectBindableResources(sbr, rs.CR, []schema.GroupVersionResource{
 			{Group: "", Version: "v1", Resource: "configmaps"},
 			{Group: "", Version: "v1", Resource: "services"},
 			{Group: "route.openshift.io", Version: "v1", Resource: "routes"},
@@ -51,36 +51,36 @@ func (r *Retriever) ReadBindableResourcesData(
 			return err
 		}
 		for k, v := range vals {
-			r.storeInto(cr, k, []byte(fmt.Sprintf("%v", v)))
+			r.storeInto(rs.EnvVarPrefix, rs.CR, k, []byte(fmt.Sprintf("%v", v)))
 		}
 	}
 
 	return nil
 }
 
-func (r *Retriever) storeInto(cr *unstructured.Unstructured, key string, value []byte) {
-	r.store(cr, key, value)
+func (r *Retriever) storeInto(envVarPrefix *string, cr *unstructured.Unstructured, key string, value []byte) {
+	r.store(envVarPrefix, cr, key, value)
 }
 
-func (r *Retriever) copyFrom(u *unstructured.Unstructured, path string, fieldPath string, descriptors []string) error {
-	if err := r.read(u, path, fieldPath, descriptors); err != nil {
+func (r *Retriever) copyFrom(envVarPrefix *string, u *unstructured.Unstructured, path string, fieldPath string, descriptors []string) error {
+	if err := r.read(envVarPrefix, u, path, fieldPath, descriptors); err != nil {
 		return err
 	}
 	return nil
 }
 
 // ReadCRDDescriptionData reads data related to given crdDescription
-func (r *Retriever) ReadCRDDescriptionData(u *unstructured.Unstructured, crdDescription *olmv1alpha1.CRDDescription) error {
+func (r *Retriever) ReadCRDDescriptionData(envVarPrefix *string, u *unstructured.Unstructured, crdDescription *olmv1alpha1.CRDDescription) error {
 	r.logger.Info("Looking for spec-descriptors in 'spec'...")
 	for _, specDescriptor := range crdDescription.SpecDescriptors {
-		if err := r.copyFrom(u, "spec", specDescriptor.Path, specDescriptor.XDescriptors); err != nil {
+		if err := r.copyFrom(envVarPrefix, u, "spec", specDescriptor.Path, specDescriptor.XDescriptors); err != nil {
 			return err
 		}
 	}
 
 	r.logger.Info("Looking for status-descriptors in 'status'...")
 	for _, statusDescriptor := range crdDescription.StatusDescriptors {
-		if err := r.copyFrom(u, "status", statusDescriptor.Path, statusDescriptor.XDescriptors); err != nil {
+		if err := r.copyFrom(envVarPrefix, u, "status", statusDescriptor.Path, statusDescriptor.XDescriptors); err != nil {
 			return err
 		}
 	}
diff --git a/pkg/controller/servicebindingrequest/retriever.go b/pkg/controller/servicebindingrequest/retriever.go
index 0a5d50b0..b59055ab 100644
--- a/pkg/controller/servicebindingrequest/retriever.go
+++ b/pkg/controller/servicebindingrequest/retriever.go
@@ -76,7 +76,7 @@ func (r *Retriever) getCRKey(u *unstructured.Unstructured, section string, key s
 // read attributes from CR, where place means which top level key name contains the "path" actual
 // value, and parsing x-descriptors in order to either directly read CR data, or read items from
 // a secret.
-func (r *Retriever) read(cr *unstructured.Unstructured, place, path string, xDescriptors []string) error {
+func (r *Retriever) read(envVarPrefix *string, cr *unstructured.Unstructured, place, path string, xDescriptors []string) error {
 	log := r.logger.WithValues(
 		"CR.Section", place,
 		"CRDDescription.Path", path,
@@ -114,7 +114,7 @@ func (r *Retriever) read(cr *unstructured.Unstructured, place, path string, xDes
 			r.markVisitedPaths(r.extractSecretItemName(xDescriptor), pathValue, place)
 			r.VolumeKeys = append(r.VolumeKeys, pathValue)
 		} else if strings.HasPrefix(xDescriptor, attributePrefix) {
-			r.store(cr, path, []byte(pathValue))
+			r.store(envVarPrefix, cr, path, []byte(pathValue))
 		} else {
 			log.Debug("Defaulting....")
 		}
@@ -122,14 +122,14 @@ func (r *Retriever) read(cr *unstructured.Unstructured, place, path string, xDes
 
 	for name, items := range secrets {
 		// loading secret items all-at-once
-		err := r.readSecret(cr, name, items, place, path)
+		err := r.readSecret(envVarPrefix, cr, name, items, place, path)
 		if err != nil {
 			return err
 		}
 	}
 	for name, items := range configMaps {
 		// add the function readConfigMap
-		err := r.readConfigMap(cr, name, items, place, path)
+		err := r.readConfigMap(envVarPrefix, cr, name, items, place, path)
 		if err != nil {
 			return err
 		}
@@ -167,7 +167,7 @@ func (r *Retriever) markVisitedPaths(name, keyPath, fromPath string) {
 
 // readSecret based in secret name and list of items, read a secret from the same namespace informed
 // in plan instance.
-func (r *Retriever) readSecret(cr *unstructured.Unstructured, name string, items []string, fromPath string, path string) error {
+func (r *Retriever) readSecret(envVarPrefix *string, cr *unstructured.Unstructured, name string, items []string, fromPath string, path string) error {
 	log := r.logger.WithValues("Secret.Name", name, "Secret.Items", items)
 	log.Debug("Reading secret items...")
 
@@ -197,7 +197,13 @@ func (r *Retriever) readSecret(cr *unstructured.Unstructured, name string, items
 		// update cache after reading configmap/secret in cache
 		r.cache[fromPath].(map[string]interface{})[path].(map[string]interface{})[k] = string(data)
 		// making sure key name has a secret reference
-		r.store(cr, fmt.Sprintf("secret_%s", k), data)
+		if envVarPrefix != nil && *envVarPrefix == "" {
+			r.store(envVarPrefix, cr, k, data)
+
+		} else {
+			r.store(envVarPrefix, cr, fmt.Sprintf("secret_%s", k), data)
+
+		}
 	}
 
 	r.Objects = append(r.Objects, secret)
@@ -206,7 +212,7 @@ func (r *Retriever) readSecret(cr *unstructured.Unstructured, name string, items
 
 // readConfigMap based in configMap name and list of items, read a configMap from the same namespace informed
 // in plan instance.
-func (r *Retriever) readConfigMap(cr *unstructured.Unstructured, name string, items []string, fromPath string, path string) error {
+func (r *Retriever) readConfigMap(envVarPrefix *string, cr *unstructured.Unstructured, name string, items []string, fromPath string, path string) error {
 	log := r.logger.WithValues("ConfigMap.Name", name, "ConfigMap.Items", items)
 	log.Debug("Reading ConfigMap items...")
 
@@ -235,7 +241,11 @@ func (r *Retriever) readConfigMap(cr *unstructured.Unstructured, name string, it
 		// update cache after reading configmap/secret in cache
 		r.cache[fromPath].(map[string]interface{})[path].(map[string]interface{})[k] = value
 		// making sure key name has a configMap reference
-		r.store(cr, fmt.Sprintf("configMap_%s", k), []byte(value))
+		if envVarPrefix != nil && *envVarPrefix == "" {
+			r.store(envVarPrefix, cr, k, []byte(value))
+		} else {
+			r.store(envVarPrefix, cr, fmt.Sprintf("configMap_%s", k), []byte(value))
+		}
 	}
 
 	r.Objects = append(r.Objects, u)
@@ -243,13 +253,25 @@ func (r *Retriever) readConfigMap(cr *unstructured.Unstructured, name string, it
 }
 
 // store key and value, formatting key to look like an environment variable.
-func (r *Retriever) store(u *unstructured.Unstructured, key string, value []byte) {
+func (r *Retriever) store(envVarPrefix *string, u *unstructured.Unstructured, key string, value []byte) {
 	key = strings.ReplaceAll(key, ":", "_")
 	key = strings.ReplaceAll(key, ".", "_")
-	if r.bindingPrefix == "" {
-		key = fmt.Sprintf("%s_%s", u.GetKind(), key)
+	if envVarPrefix == nil {
+		if r.bindingPrefix == "" {
+			key = fmt.Sprintf("%s_%s", u.GetKind(), key)
+		} else {
+			key = fmt.Sprintf("%s_%s_%s", r.bindingPrefix, u.GetKind(), key)
+		}
+	} else if *envVarPrefix == "" {
+		if r.bindingPrefix != "" {
+			key = fmt.Sprintf("%s_%s", r.bindingPrefix, key)
+		}
 	} else {
-		key = fmt.Sprintf("%s_%s_%s", r.bindingPrefix, u.GetKind(), key)
+		if r.bindingPrefix != "" {
+			key = fmt.Sprintf("%s_%s_%s", r.bindingPrefix, *envVarPrefix, key)
+		} else {
+			key = fmt.Sprintf("%s_%s", *envVarPrefix, key)
+		}
 	}
 	key = strings.ToUpper(key)
 	r.data[key] = value
diff --git a/pkg/controller/servicebindingrequest/sbrcontroller.go b/pkg/controller/servicebindingrequest/sbrcontroller.go
index bc61e287..a6f82b4d 100644
--- a/pkg/controller/servicebindingrequest/sbrcontroller.go
+++ b/pkg/controller/servicebindingrequest/sbrcontroller.go
@@ -147,7 +147,7 @@ func (s *SBRController) AddWatchForGVK(gvk schema.GroupVersionKind) error {
 // addCSVWatch creates a watch on ClusterServiceVersion.
 func (s *SBRController) addCSVWatch() error {
 	log := s.logger
-	gvr := olmv1alpha1.SchemeGroupVersion.WithResource(ClusterServiceVersionKind)
+	gvr := olmv1alpha1.SchemeGroupVersion.WithResource(csvResource)
 	resourceClient := s.Client.Resource(gvr).Namespace(os.Getenv("WATCH_NAMESPACE"))
 	_, err := resourceClient.List(metav1.ListOptions{})
 	if err != nil && errors.IsNotFound(err) {


Test patch:
diff --git a/pkg/controller/servicebindingrequest/binder_test.go b/pkg/controller/servicebindingrequest/binder_test.go
index c6f683ed..4b0fa6d5 100644
--- a/pkg/controller/servicebindingrequest/binder_test.go
+++ b/pkg/controller/servicebindingrequest/binder_test.go
@@ -2,6 +2,7 @@ package servicebindingrequest
 
 import (
 	"context"
+	"fmt"
 	"testing"
 	"time"
 
@@ -13,6 +14,7 @@ import (
 	knativev1 "knative.dev/serving/pkg/apis/serving/v1"
 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
 
+	"github.com/redhat-developer/service-binding-operator/pkg/converter"
 	"github.com/redhat-developer/service-binding-operator/test/mocks"
 )
 
@@ -131,6 +133,28 @@ func TestBinderNew(t *testing.T) {
 		parsedTime, err := time.Parse(time.RFC3339, envVar.Value)
 		require.NoError(t, err)
 		require.True(t, parsedTime.Before(time.Now()))
+
+		// test binder with extra modifier present
+		ch := make(chan struct{})
+		binder.modifier = ExtraFieldsModifierFunc(func(u *unstructured.Unstructured) error {
+			close(ch)
+			return nil
+		})
+
+		list, err = binder.search()
+		require.NoError(t, err)
+		require.Equal(t, 1, len(list.Items))
+
+		updatedObjects, err = binder.update(list)
+		require.NoError(t, err)
+		require.Len(t, updatedObjects, 1)
+		<-ch
+
+		// call another update as object is already updated, modifier func should not be called
+		updatedObjects, err = binder.update(list)
+		require.NoError(t, err)
+		require.Len(t, updatedObjects, 0)
+		binder.modifier = nil
 	})
 
 	t.Run("remove", func(t *testing.T) {
@@ -300,6 +324,7 @@ func TestKnativeServicesContractWithBinder(t *testing.T) {
 	)
 
 	require.NotNil(t, binder)
+	require.NotNil(t, binder.modifier)
 
 	t.Run("Knative service contract with service binding operator", func(t *testing.T) {
 		list, err := binder.search()
@@ -308,4 +333,78 @@ func TestKnativeServicesContractWithBinder(t *testing.T) {
 
 	})
 
+	ksvc := mocks.KnativeServiceMock(ns, "knative-app-with-rev-name", matchLabels)
+	ksvc.Spec.Template.Name = "knative-app-with-rev-name-revision-1"
+
+}
+
+func Test_extraFieldsModifier(t *testing.T) {
+	ns := "binder"
+	name := "service-binding-request"
+	matchLabels := map[string]string{
+		"connects-to": "database",
+		"environment": "binder",
+	}
+
+	f := mocks.NewFake(t, ns)
+	deploy := mocks.DeploymentMock(ns, "deployment-fake", matchLabels)
+	sbr := mocks.ServiceBindingRequestMock(ns, name, nil, "", deploy.Name, deploymentsGVR, matchLabels)
+	binder := NewBinder(
+		context.TODO(),
+		f.FakeClient(),
+		f.FakeDynClient(),
+		sbr,
+		[]string{},
+	)
+
+	require.NotNil(t, binder)
+	require.Nil(t, binder.modifier)
+
+	gvr := knativev1.SchemeGroupVersion.WithResource("services")
+	ksvc := mocks.KnativeServiceMock(ns, "knative-app-with-rev-name", matchLabels)
+	sbr = mocks.ServiceBindingRequestMock(ns, name, nil, "", ksvc.Name, gvr, matchLabels)
+
+	binder = NewBinder(
+		context.TODO(),
+		f.FakeClient(),
+		f.FakeDynClient(),
+		sbr,
+		[]string{},
+	)
+
+	require.NotNil(t, binder)
+	require.NotNil(t, binder.modifier)
+
+	t.Run("ksvc revision name is empty", func(t *testing.T) {
+		u, err := converter.ToUnstructured(&ksvc)
+		require.NoError(t, err)
+
+		err = binder.modifier.ModifyExtraFields(u)
+		require.NoError(t, err)
+
+		var modified knativev1.Service
+		err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &modified)
+		require.NoError(t, err)
+		assert.Equal(t, ksvc, modified)
+	})
+
+	t.Run("ksvc revision name is not empty", func(t *testing.T) {
+		ksvc.Spec.Template.Name = fmt.Sprintf("%s-%s", ksvc.Name, "rev-1")
+
+		u, err := converter.ToUnstructured(&ksvc)
+		require.NoError(t, err)
+
+		err = binder.modifier.ModifyExtraFields(u)
+		require.NoError(t, err)
+
+		var modified knativev1.Service
+		err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &modified)
+		require.NoError(t, err)
+		assert.Equal(t, "", modified.Spec.Template.Name)
+
+		ksvc.Spec.Template.Name = ""
+		// the rest fields shoud not be modified
+		assert.Equal(t, ksvc, modified)
+	})
+
 }
diff --git a/pkg/controller/servicebindingrequest/binding_test.go b/pkg/controller/servicebindingrequest/binding_test.go
index 7c27886d..45537697 100644
--- a/pkg/controller/servicebindingrequest/binding_test.go
+++ b/pkg/controller/servicebindingrequest/binding_test.go
@@ -545,7 +545,7 @@ func TestServiceBinder_Bind(t *testing.T) {
 			SBR:                    sbrEmptyAppSelector,
 			Client:                 f.FakeClient(),
 		},
-		wantBuildErr: EmptyApplicationSelectorErr,
+		wantErr: EmptyApplicationSelectorErr,
 		wantConditions: []wantedCondition{
 			{
 				Type:    conditions.BindingReady,
diff --git a/pkg/controller/servicebindingrequest/reconciler_test.go b/pkg/controller/servicebindingrequest/reconciler_test.go
index 525503ed..929dae0a 100644
--- a/pkg/controller/servicebindingrequest/reconciler_test.go
+++ b/pkg/controller/servicebindingrequest/reconciler_test.go
@@ -229,14 +229,14 @@ func TestReconcilerGenericBinding(t *testing.T) {
 	// Reconcile without deployment
 	res, err := reconciler.Reconcile(reconcileRequest())
 	require.NoError(t, err)
-	require.True(t, res.Requeue)
+	require.False(t, res.Requeue)
 
 	namespacedName := types.NamespacedName{Namespace: reconcilerNs, Name: reconcilerName}
 	sbrOutput, err := reconciler.getServiceBindingRequest(namespacedName)
 	require.NoError(t, err)
 
 	require.Equal(t, conditions.BindingReady, sbrOutput.Status.Conditions[0].Type)
-	require.Equal(t, corev1.ConditionFalse, sbrOutput.Status.Conditions[0].Status)
+	require.Equal(t, corev1.ConditionTrue, sbrOutput.Status.Conditions[0].Status)
 	require.Equal(t, 0, len(sbrOutput.Status.Applications))
 
 	// Reconcile with deployment
@@ -347,13 +347,15 @@ func TestEmptyApplicationSelector(t *testing.T) {
 
 	res, err := reconciler.Reconcile(reconcileRequest())
 	require.NoError(t, err)
-	require.True(t, res.Requeue)
+	require.False(t, res.Requeue)
 
 	namespacedName := types.NamespacedName{Namespace: reconcilerNs, Name: reconcilerName}
 	sbrOutput, err := reconciler.getServiceBindingRequest(namespacedName)
 	require.NoError(t, err)
 
 	require.Equal(t, conditions.BindingReady, sbrOutput.Status.Conditions[0].Type)
-	require.Equal(t, corev1.ConditionFalse, sbrOutput.Status.Conditions[0].Status)
+	// Currently the Conditions[0].Status would be true as application's absence won't cause error
+	// TODO New steps to conditions to be introduced - InjectionReady, CollectionReady
+	require.Equal(t, corev1.ConditionTrue, sbrOutput.Status.Conditions[0].Status)
 	require.Equal(t, 0, len(sbrOutput.Status.Applications))
 }
diff --git a/pkg/controller/servicebindingrequest/retriever_test.go b/pkg/controller/servicebindingrequest/retriever_test.go
index 607e833a..0f3411fd 100644
--- a/pkg/controller/servicebindingrequest/retriever_test.go
+++ b/pkg/controller/servicebindingrequest/retriever_test.go
@@ -16,6 +16,8 @@ func TestRetriever(t *testing.T) {
 	ns := "testing"
 	backingServiceNs := "backing-servicec-ns"
 	crName := "db-testing"
+	testEnvVarPrefix := "TEST_PREFIX"
+	emptyEnvVarPrefix := ""
 
 	f := mocks.NewFake(t, ns)
 	f.AddMockedUnstructuredCSV("csv")
@@ -56,24 +58,24 @@ func TestRetriever(t *testing.T) {
 
 	t.Run("read", func(t *testing.T) {
 		// reading from secret, from status attribute
-		err := retriever.read(cr, "status", "dbCredentials", []string{
+		err := retriever.read(&testEnvVarPrefix, cr, "status", "dbCredentials", []string{
 			"binding:env:object:secret:user",
 			"binding:env:object:secret:password",
 		})
 		require.NoError(t, err)
 
 		t.Logf("retriever.data '%#v'", retriever.data)
-		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_SECRET_USER")
-		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_SECRET_PASSWORD")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_SECRET_USER")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_SECRET_PASSWORD")
 
 		// reading from spec attribute
-		err = retriever.read(cr, "spec", "image", []string{
+		err = retriever.read(&testEnvVarPrefix, cr, "spec", "image", []string{
 			"binding:env:attribute",
 		})
 		require.NoError(t, err)
 
 		t.Logf("retriever.data '%#v'", retriever.data)
-		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_IMAGE")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_IMAGE")
 
 	})
 
@@ -85,30 +87,86 @@ func TestRetriever(t *testing.T) {
 	t.Run("readSecret", func(t *testing.T) {
 		retriever.data = make(map[string][]byte)
 
-		err := retriever.readSecret(cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
+		err := retriever.readSecret(&testEnvVarPrefix, cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
 		require.NoError(t, err)
 
-		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_SECRET_USER")
-		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_SECRET_PASSWORD")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_SECRET_USER")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_SECRET_PASSWORD")
 	})
 
 	t.Run("store", func(t *testing.T) {
-		retriever.store(cr, "test", []byte("test"))
-		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_TEST")
-		require.Equal(t, []byte("test"), retriever.data["SERVICE_BINDING_DATABASE_TEST"])
+		retriever.store(&testEnvVarPrefix, cr, "test", []byte("test"))
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_TEST")
+		require.Equal(t, []byte("test"), retriever.data["SERVICE_BINDING_TEST_PREFIX_TEST"])
 	})
 
-	t.Run("empty prefix", func(t *testing.T) {
+	t.Run("non-empty SBR prefix and nil service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "SERVICE_BINDING")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readSecret(nil, cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_SECRET_USER")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_SECRET_PASSWORD")
+	})
+	t.Run("empty SBR prefix and nil service prefix", func(t *testing.T) {
 		retriever = NewRetriever(fakeDynClient, plan, "")
 		require.NotNil(t, retriever)
 		retriever.data = make(map[string][]byte)
 
-		err := retriever.readSecret(cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
+		err := retriever.readSecret(nil, cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
 		require.NoError(t, err)
 
 		require.Contains(t, retriever.data, "DATABASE_SECRET_USER")
 		require.Contains(t, retriever.data, "DATABASE_SECRET_PASSWORD")
 	})
+
+	t.Run("non-empty SBR prefix and non-empty service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "SERVICE_BINDING")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readSecret(&testEnvVarPrefix, cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_SECRET_USER")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_SECRET_PASSWORD")
+	})
+	t.Run("non-empty SBR prefix and empty service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "SERVICE_BINDING")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readSecret(&emptyEnvVarPrefix, cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "SERVICE_BINDING_USER")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_PASSWORD")
+	})
+	t.Run("empty SBR prefix and non-empty service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readSecret(&testEnvVarPrefix, cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "TEST_PREFIX_SECRET_USER")
+		require.Contains(t, retriever.data, "TEST_PREFIX_SECRET_PASSWORD")
+	})
+	t.Run("empty SBR prefix and empty service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readSecret(&emptyEnvVarPrefix, cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "USER")
+		require.Contains(t, retriever.data, "PASSWORD")
+	})
 }
 
 func TestRetrieverWithNestedCRKey(t *testing.T) {
@@ -168,6 +226,8 @@ func TestRetrieverWithConfigMap(t *testing.T) {
 
 	ns := "testing"
 	crName := "db-testing"
+	testEnvVarPrefix := "TEST_PREFIX"
+	emptyEnvVarPrefix := ""
 
 	f := mocks.NewFake(t, ns)
 	f.AddMockedUnstructuredCSV("csv")
@@ -197,15 +257,15 @@ func TestRetrieverWithConfigMap(t *testing.T) {
 
 	t.Run("read", func(t *testing.T) {
 		// reading from configMap, from status attribute
-		err = retriever.read(cr, "spec", "dbConfigMap", []string{
+		err = retriever.read(&testEnvVarPrefix, cr, "spec", "dbConfigMap", []string{
 			"binding:env:object:configmap:user",
 			"binding:env:object:configmap:password",
 		})
 		require.NoError(t, err)
 
 		t.Logf("retriever.data '%#v'", retriever.data)
-		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_CONFIGMAP_USER")
-		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_CONFIGMAP_PASSWORD")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_CONFIGMAP_USER")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_CONFIGMAP_PASSWORD")
 	})
 
 	t.Run("extractConfigMapItemName", func(t *testing.T) {
@@ -216,10 +276,78 @@ func TestRetrieverWithConfigMap(t *testing.T) {
 	t.Run("readConfigMap", func(t *testing.T) {
 		retriever.data = make(map[string][]byte)
 
-		err := retriever.readConfigMap(cr, crName, []string{"user", "password"}, "spec", "dbConfigMap")
+		err := retriever.readConfigMap(&testEnvVarPrefix, cr, crName, []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, ("SERVICE_BINDING_TEST_PREFIX_CONFIGMAP_USER"))
+		require.Contains(t, retriever.data, ("SERVICE_BINDING_TEST_PREFIX_CONFIGMAP_PASSWORD"))
+	})
+
+	t.Run("non-empty SBR prefix and nil service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "SERVICE_BINDING")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readConfigMap(nil, cr, crName, []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_CONFIGMAP_USER")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_CONFIGMAP_PASSWORD")
+	})
+	t.Run("empty SBR prefix and nil service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readConfigMap(nil, cr, crName, []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "DATABASE_CONFIGMAP_USER")
+		require.Contains(t, retriever.data, "DATABASE_CONFIGMAP_PASSWORD")
+	})
+
+	t.Run("non-empty SBR prefix and non-empty service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "SERVICE_BINDING")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readConfigMap(&testEnvVarPrefix, cr, crName, []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_CONFIGMAP_USER")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_TEST_PREFIX_CONFIGMAP_PASSWORD")
+	})
+	t.Run("non-empty SBR prefix and empty service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "SERVICE_BINDING")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readConfigMap(&emptyEnvVarPrefix, cr, crName, []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "SERVICE_BINDING_USER")
+		require.Contains(t, retriever.data, "SERVICE_BINDING_PASSWORD")
+	})
+	t.Run("empty SBR prefix and non-empty service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readConfigMap(&testEnvVarPrefix, cr, crName, []string{"user", "password"}, "spec", "dbConfigMap")
+		require.NoError(t, err)
+
+		require.Contains(t, retriever.data, "TEST_PREFIX_CONFIGMAP_USER")
+		require.Contains(t, retriever.data, "TEST_PREFIX_CONFIGMAP_PASSWORD")
+	})
+	t.Run("empty SBR prefix and empty service prefix", func(t *testing.T) {
+		retriever = NewRetriever(fakeDynClient, plan, "")
+		require.NotNil(t, retriever)
+		retriever.data = make(map[string][]byte)
+
+		err := retriever.readConfigMap(&emptyEnvVarPrefix, cr, crName, []string{"user", "password"}, "spec", "dbConfigMap")
 		require.NoError(t, err)
 
-		require.Contains(t, retriever.data, ("SERVICE_BINDING_DATABASE_CONFIGMAP_USER"))
-		require.Contains(t, retriever.data, ("SERVICE_BINDING_DATABASE_CONFIGMAP_PASSWORD"))
+		require.Contains(t, retriever.data, "USER")
+		require.Contains(t, retriever.data, "PASSWORD")
 	})
 }