Repository: redhat-developer/service-binding-operator
Pull request #334 (2020-02-14 06:47:52)

Refactor knative test: abridge test by removing unnecessary assertion.
### Motivation

Refactor knative test

### Testing
`go test ./pkg/controller/servicebindingrequest -run TestKnativeServicesContractWithBinder -v` 
or
`make test-unit` to run all the unit tests 

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 **otaviof**
You can assign the PR to them by writing `/assign @otaviof` 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":["otaviof"]} -->
codecov[bot]: # [Codecov](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/334?src=pr&el=h1) Report
> Merging [#334](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/334?src=pr&el=desc) into [master](https://codecov.io/gh/redhat-developer/service-binding-operator/commit/51714086626d7203c0b7fc530ab7b23a5deb92f4?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

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

```diff
@@           Coverage Diff           @@
##           master     #334   +/-   ##
=======================================
  Coverage   52.82%   52.82%           
=======================================
  Files          21       21           
  Lines        1467     1467           
=======================================
  Hits          775      775           
  Misses        559      559           
  Partials      133      133
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/334?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/334?src=pr&el=footer). Last update [5171408...c498d4d](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/334?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.travis.yml b/.travis.yml
index 89f3151a..ca5ea17e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,7 +11,7 @@ git:
 
 env:
   global:
-    - SDK_VERSION="0.15.1"
+    - SDK_VERSION="0.15.2"
     - GO111MODULE=on
     - USER="redhat-developer"
     - EMAIL="openshift-dev-services@redhat.com"
@@ -35,9 +35,9 @@ before_install:
 script:
   # Run merge-to-master-release
   - if [ "$TRAVIS_BRANCH" = "master" ]; then
-    make merge-to-master-release;
-    make push-to-manifest-repo;
-    make push-bundle-to-quay;
+    make merge-to-master-release &&
+    make push-to-manifest-repo &&
+    make push-bundle-to-quay || exit 1;
     fi
 
 after_success:
diff --git a/Dockerfile.rhel b/Dockerfile.rhel
index 4ca6eee7..7c755827 100644
--- a/Dockerfile.rhel
+++ b/Dockerfile.rhel
@@ -16,7 +16,6 @@ COPY build build
 COPY cmd cmd
 COPY deploy deploy
 COPY hack hack
-COPY manifests manifests
 COPY pkg pkg
 COPY test test
 COPY vendor vendor
diff --git a/Makefile b/Makefile
index c1c1aff1..b70cc297 100644
--- a/Makefile
+++ b/Makefile
@@ -126,7 +126,7 @@ BUNDLE_VERSION ?= $(OPERATOR_VERSION)-$(COMMIT_COUNT)
 BASE_BUNDLE_VERSION ?= 0.0.23
 OPERATOR_IMAGE_REF ?= $(OPERATOR_IMAGE_REL):$(GIT_COMMIT_ID)
 CSV_PACKAGE_NAME ?= $(GO_PACKAGE_REPO_NAME)
-CSV_CREATION_TIMESTAMP ?= $(shell date '+%FT%T%Z')
+CSV_CREATION_TIMESTAMP ?= $(shell TZ=GMT date '+%FT%TZ')
 
 QUAY_TOKEN ?= ""
 QUAY_BUNDLE_TOKEN ?= ""
@@ -435,6 +435,8 @@ push-to-manifest-repo:
 	awk -i inplace '!/^[[:space:]]+replaces:[[:space:]]+[[:graph:]]+/ { print $0 }' $(MANIFESTS_TMP)/${BUNDLE_VERSION}/*.clusterserviceversion.yaml
 	sed -i -e 's,BUNDLE_VERSION,$(BUNDLE_VERSION),g' $(MANIFESTS_TMP)/*.yaml
 	sed -i -e 's,PACKAGE_NAME,$(CSV_PACKAGE_NAME),g' $(MANIFESTS_TMP)/*.yaml
+	sed -i -e 's,ICON_BASE64_DATA,$(shell base64 --wrap=0 ./assets/icon/red-hat-logo.svg),g' $(MANIFESTS_TMP)/${BUNDLE_VERSION}/*.clusterserviceversion.yaml
+	sed -i -e 's,ICON_MEDIA_TYPE,image/svg+xml,g' $(MANIFESTS_TMP)/${BUNDLE_VERSION}/*.clusterserviceversion.yaml
 
 ## -- Target for pushing manifest bundle to quay application --
 .PHONY: push-bundle-to-quay
diff --git a/assets/icon/red-hat-logo.svg b/assets/icon/red-hat-logo.svg
new file mode 100644
index 00000000..062df671
--- /dev/null
+++ b/assets/icon/red-hat-logo.svg
@@ -0,0 +1,1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 145"><defs><style>.cls-1{fill:#e00;}</style></defs><title>RedHat-Logo-Hat-Color</title><path d="M157.77,62.61a14,14,0,0,1,.31,3.42c0,14.88-18.1,17.46-30.61,17.46C78.83,83.49,42.53,53.26,42.53,44a6.43,6.43,0,0,1,.22-1.94l-3.66,9.06a18.45,18.45,0,0,0-1.51,7.33c0,18.11,41,45.48,87.74,45.48,20.69,0,36.43-7.76,36.43-21.77,0-1.08,0-1.94-1.73-10.13Z"/><path class="cls-1" d="M127.47,83.49c12.51,0,30.61-2.58,30.61-17.46a14,14,0,0,0-.31-3.42l-7.45-32.36c-1.72-7.12-3.23-10.35-15.73-16.6C124.89,8.69,103.76.5,97.51.5,91.69.5,90,8,83.06,8c-6.68,0-11.64-5.6-17.89-5.6-6,0-9.91,4.09-12.93,12.5,0,0-8.41,23.72-9.49,27.16A6.43,6.43,0,0,0,42.53,44c0,9.22,36.3,39.45,84.94,39.45M160,72.07c1.73,8.19,1.73,9.05,1.73,10.13,0,14-15.74,21.77-36.43,21.77C78.54,104,37.58,76.6,37.58,58.49a18.45,18.45,0,0,1,1.51-7.33C22.27,52,.5,55,.5,74.22c0,31.48,74.59,70.28,133.65,70.28,45.28,0,56.7-20.48,56.7-36.65,0-12.72-11-27.16-30.83-35.78"/></svg>
\ No newline at end of file
diff --git a/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml b/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml
index d5ac4c97..bb2a33e8 100644
--- a/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml
+++ b/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml
@@ -116,6 +116,28 @@ spec:
               - resourceRef
               - version
               type: object
+            backingServiceSelectors:
+              description: BackingServiceSelectors is used to identify multiple backing
+                services.
+              items:
+                description: BackingServiceSelector defines the selector based on
+                  resource name, version, and resource kind
+                properties:
+                  group:
+                    type: string
+                  kind:
+                    type: string
+                  resourceRef:
+                    type: string
+                  version:
+                    type: string
+                required:
+                - group
+                - kind
+                - resourceRef
+                - version
+                type: object
+              type: array
             customEnvVar:
               description: Custom env variables
               items:
@@ -226,7 +248,6 @@ spec:
               type: string
           required:
           - applicationSelector
-          - backingServiceSelector
           type: object
         status:
           description: ServiceBindingRequestStatus defines the observed state of ServiceBindingRequest
diff --git a/deploy/olm-catalog/service-binding-operator/0.0.23/service-binding-operator.v0.0.23.clusterserviceversion.yaml b/deploy/olm-catalog/service-binding-operator/0.0.23/service-binding-operator.v0.0.23.clusterserviceversion.yaml
index 03966095..b7f0112b 100644
--- a/deploy/olm-catalog/service-binding-operator/0.0.23/service-binding-operator.v0.0.23.clusterserviceversion.yaml
+++ b/deploy/olm-catalog/service-binding-operator/0.0.23/service-binding-operator.v0.0.23.clusterserviceversion.yaml
@@ -37,6 +37,9 @@ metadata:
   name: service-binding-operator.v0.0.23
   namespace: placeholder
 spec:
+  icon:
+  - base64data: ICON_BASE64_DATA
+    mediatype: ICON_MEDIA_TYPE
   apiservicedefinitions: {}
   customresourcedefinitions:
     owned:
@@ -181,8 +184,8 @@ spec:
     - name: Blog post
       url: https://developers.redhat.com/blog/2019/12/19/introducing-the-service-binding-operator
   maintainers:
-    - email: shbose@redhat.com
-      name: Shoubhik Bose
+    - email: service-binding-support@redhat.com
+      name: Openshift Application Services
   provider:
     name: Red Hat, Inc.
   version: 0.0.23
diff --git a/docs/OperatorBestPractices.md b/docs/OperatorBestPractices.md
index b24773da..e297be88 100644
--- a/docs/OperatorBestPractices.md
+++ b/docs/OperatorBestPractices.md
@@ -32,7 +32,7 @@ There are three methods for making Operator Managed Backing Service Bindable:
 
 * [Operator Providing Metadata in CRD Annotations](#operator-providing-metadata-in-crd-annotations)
 * [Operator Providing Metadata in OLM](#operator-providing-metadata-in-olm)
-* [Operator Not Providing Metadata](#operator-not-providing-metadata) Coming Soon!
+* [Operator Not Providing Metadata](#operator-not-providing-metadata)
 
 ### Operator Providing Metadata in CRD Annotations
 
diff --git a/examples/commons.mk b/examples/commons.mk
index e7e12124..a1e1ac66 100644
--- a/examples/commons.mk
+++ b/examples/commons.mk
@@ -82,11 +82,6 @@ help: ## Credit: https://gist.github.com/prwhite/8168133#gistcomment-2749866
 
 ## --- Service Binding Operator ---
 
-.PHONY: install-service-binding-operator-source
-## Install the Service Binding Operator Source
-install-service-binding-operator-source:
-	${Q}${EC} install_service_binding_operator_source
-
 .PHONY: install-service-binding-operator-subscription
 ## Install the Service Binding Operator Subscription
 install-service-binding-operator-subscription:
@@ -94,12 +89,7 @@ install-service-binding-operator-subscription:
 
 .PHONY: install-service-binding-operator
 ## Install the Service Binding Operator
-install-service-binding-operator: install-service-binding-operator-source install-service-binding-operator-subscription
-
-.PHONY: uninstall-service-binding-operator-source
-## Uninstall the Service Binding Operator Source
-uninstall-service-binding-operator-source:
-	${Q}${EC} uninstall_service_binding_operator_source
+install-service-binding-operator: install-service-binding-operator-subscription
 
 .PHONY: uninstall-service-binding-operator-subscription
 ## Uninstall the Service Binding Operator Subscription
@@ -108,7 +98,7 @@ uninstall-service-binding-operator-subscription:
 
 .PHONY: uninstall-service-binding-operator
 ## Uninstall the Service Binding Operator
-uninstall-service-binding-operator: uninstall-service-binding-operator-subscription uninstall-service-binding-operator-source
+uninstall-service-binding-operator: uninstall-service-binding-operator-subscription
 
 ## --- Backing Service DB (PostgreSQL) Operator ---
 
diff --git a/examples/java_postgresql_customvar/README.md b/examples/java_postgresql_customvar/README.md
index ae6d0deb..f93ffab7 100644
--- a/examples/java_postgresql_customvar/README.md
+++ b/examples/java_postgresql_customvar/README.md
@@ -30,11 +30,17 @@ demonstrate a sample use case.
 
 #### Install the Service Binding Operator
 
-Navigate to the `Operators`->`OperatorHub` in the OpenShift console and in the `Other` category select the `Service Bidning Operator` operator
+Navigate to the `Operators`->`OperatorHub` in the OpenShift console and in the `Developer Tools` category select the `Service Binding Operator` operator
 
 ![Service Binding Operator as shown in OperatorHub](../../assets/operator-hub-sbo-screenshot.png)
 
-and install a `stable` version.
+and install a `alpha` version.
+
+Alternatively, you can perform the same task with this make command:
+
+``` shell
+make install-service-binding-operator
+```
 
 This makes the `ServiceBindingRequest` custom resource available, that the application developer will use later.
 
diff --git a/examples/knative_postgresql_customvar/README.md b/examples/knative_postgresql_customvar/README.md
index 5ed2ca4b..d64fa9df 100644
--- a/examples/knative_postgresql_customvar/README.md
+++ b/examples/knative_postgresql_customvar/README.md
@@ -32,11 +32,17 @@ demonstrate a sample use case.
 
 #### Install the Service Binding Operator
 
-Navigate to the `Operators`->`OperatorHub` in the OpenShift console and in the `Other` category select the `Service Binding Operator` operator
+Navigate to the `Operators`->`OperatorHub` in the OpenShift console and in the `Developer Tools` category select the `Service Binding Operator` operator
 
 ![Service Binding Operator as shown in OperatorHub](../../assets/operator-hub-sbo-screenshot.png)
 
-and install a `stable` version.
+and install a `alpha` version.
+
+Alternatively, you can perform the same task with this make command:
+
+``` shell
+make install-service-binding-operator
+```
 
 This makes the `ServiceBindingRequest` custom resource available, that the application developer will use later.
 
@@ -53,6 +59,7 @@ This makes the `Database` custom resource available, that the application develo
 Navigate to the `Operators`->`OperatorHub` in the OpenShift console and in the `Cloud Provider` category select the `OpenShift Serverless Operator` operator.
 
 Note that installing this operator will automatically install this set of operators:
+
 * Elasticsearch Operator
 * Jaeger Operator
 * Kiali Operator
diff --git a/examples/multiple_services/README.md b/examples/multiple_services/README.md
new file mode 100644
index 00000000..cdfd1f44
--- /dev/null
+++ b/examples/multiple_services/README.md
@@ -0,0 +1,157 @@
+# Multiple Services Binding Example
+
+This document describes step-by-step the actions to create the required
+infrastructure to demonstrate multiple services binding built on existing
+Postgres and ETCD examples.
+
+As *Cluster Administrator*, the reader will install both the "PostgreSQL
+Database" and the "ETCD" operators, as described below.
+
+Once the cluster setup is finished, the reader will create a Postgres
+database and a ETCD cluster, and bind services to a Node application as
+a *Developer*.
+
+## Cluster Configuration
+
+### Create a New Project
+
+Create a new project, in this example it is called `multiple-services-demo`.
+
+### Install the Postgres Operator
+
+Switch to the *Administrator* perspective.
+
+Add an extra OperatorSource by pushing the "+" button on the top right corner
+and pasting the following:
+
+```yaml
+---
+apiVersion: operators.coreos.com/v1
+kind: OperatorSource
+metadata:
+  name: db-operators
+  namespace: openshift-marketplace
+spec:
+  type: appregistry
+  endpoint: https://quay.io/cnr
+  registryNamespace: pmacik
+```
+
+Go to "Operators > OperatorHub", search for "Postgres" and install "PostgreSQL
+Database" provided by Red Hat.
+
+Select "A specific namespace on the cluster" in "Installation Mode", select the
+"multiple-services-demo" namespace in "Installed Namespace" and push "Subscribe".
+
+### Install the ETCD Operator
+
+Go to "Operators > OperatorHub", search for "etcd" and install "etcd" provided by
+CNCF.
+
+Select "A specific namespace on the cluster" in "Installation Mode", select the
+"multiple-services-demo" namespace in "Installed Namespace" and push "Subscribe".
+
+## Application Configuration
+
+Switch to the *Developer* perspective.
+
+Create the Postgres database `db-demo` by pushing the "+" button on the top right
+corner and pasting the following:
+
+```yaml
+---
+apiVersion: postgresql.baiju.dev/v1alpha1
+kind: Database
+metadata:
+  name: db-demo
+spec:
+  image: docker.io/postgres
+  imageName: postgres
+  dbName: db-demo
+```
+
+Create the ETCD cluster `etcd-demo` by pushing the "+" button on the top right
+corner and pasting the following:
+
+```yaml
+---
+apiVersion: etcd.database.coreos.com/v1beta2
+kind: EtcdCluster
+metadata:
+ name: etcd-demo
+spec:
+ size: 5
+ version: "3.2.13"
+```
+
+Import the application by pushing the "+Add" button on the left side of the
+screen, and then the "From Git" button. Fill the "Git Repo URL" with
+`https://github.com/akashshinde/node-todo.git`; the repository will be
+validated and the appropriate "Builder Image" and "Builder Image Version"
+will be selected. Push the "Create" button to create the application.
+
+Create the ServiceBindingRequest `node-todo-git` by pushing the "+" button
+on the top right corner and pasting the following:
+
+```yaml
+---
+apiVersion: apps.openshift.io/v1alpha1
+kind: ServiceBindingRequest
+metadata:
+  name: node-todo-git
+spec:
+  applicationSelector:
+    resourceRef: node-todo-git
+    group: apps
+    version: v1
+    resource: deployments
+  backingServiceSelectors:
+  - group: postgresql.baiju.dev
+    version: v1alpha1
+    kind: Database
+    resourceRef: db-demo
+  - group: etcd.database.coreos.com
+    version: v1beta2
+    kind: EtcdCluster
+    resourceRef: etcd-demo
+  detectBindingResources: true
+```
+
+Once the binding is processed, the secret can be verified by executing
+`kubectl get secrets node-todo-git -o yaml`:
+
+```yaml
+apiVersion: v1
+data:
+  DATABASE_CLUSTERIP: MTcyLjMwLjcyLjg5
+  DATABASE_CONFIGMAP_DB_HOST: MTcyLjMwLjcyLjg5
+  DATABASE_CONFIGMAP_DB_NAME: ZGItZGVtbw==
+  DATABASE_CONFIGMAP_DB_PASSWORD: cGFzc3dvcmQ=
+  DATABASE_CONFIGMAP_DB_PORT: NTQzMg==
+  DATABASE_CONFIGMAP_DB_USERNAME: cG9zdGdyZXM=
+  DATABASE_CONFIGMAP_PASSWORD: cGFzc3dvcmQ=
+  DATABASE_CONFIGMAP_USER: cG9zdGdyZXM=
+  DATABASE_DB_HOST: MTcyLjMwLjcyLjg5
+  DATABASE_DB_NAME: ZGItZGVtbw==
+  DATABASE_DB_PASSWORD: cGFzc3dvcmQ=
+  DATABASE_DB_PORT: NTQzMg==
+  DATABASE_DB_USERNAME: cG9zdGdyZXM=
+  DATABASE_DBCONNECTIONIP: MTcyLjMwLjcyLjg5
+  DATABASE_DBCONNECTIONPORT: NTQzMg==
+  DATABASE_DBNAME: ZGItZGVtbw==
+  DATABASE_SECRET_PASSWORD: cGFzc3dvcmQ=
+  DATABASE_SECRET_USER: cG9zdGdyZXM=
+  ETCDCLUSTER_CLUSTERIP: MTcyLjMwLjYyLjUy
+kind: Secret
+metadata:
+  annotations:
+    service-binding-operator.apps.openshift.io/binding-name: node-todo-git
+    service-binding-operator.apps.openshift.io/binding-namespace: multiple-services-demo
+  creationTimestamp: "2020-02-14T11:58:29Z"
+  name: node-todo-git
+  namespace: multiple-services-demo
+  resourceVersion: "257567"
+  selfLink: /api/v1/namespaces/multiple-services-demo/secrets/node-todo-git
+  uid: 15aafcae-d334-49d8-be4c-2331f9c7cffe
+type: Opaque
+```
diff --git a/examples/nodejs_awsrds_varprefix/README.md b/examples/nodejs_awsrds_varprefix/README.md
index 19bca63a..b9f8ce49 100644
--- a/examples/nodejs_awsrds_varprefix/README.md
+++ b/examples/nodejs_awsrds_varprefix/README.md
@@ -22,15 +22,21 @@ The Backing Service Operator represents a database required by the
 applications. We'll use [aws-rds-operator](https://github.com/operator-backing-service-samples/aws-rds) to
 manage off-cluster RDS database instances on AWS.
 
-#### Install the Service Binding Operator using an `OperatorSource`
+#### Install the Service Binding Operator
 
-Navigate to the `Operators`->`OperatorHub` in the OpenShift console under the `openshift-marketplace` project and in the `Other` category select the `Service Bidning Operator` operator
+Navigate to the `Operators`->`OperatorHub` in the OpenShift console and in the `Developer Tools` category select the `Service Binding Operator` operator
 
 ![Service Binding Operator as shown in OperatorHub](../../assets/operator-hub-sbo-screenshot.png)
 
-and install a `stable` version.
+and install a `alpha` version.
 
-This makes the `ServiceBindingRequest` custom resource available, that we as the application developer will [use later](#bind-the-db-to-the-shell-application).
+Alternatively, you can perform the same task with this make command:
+
+``` shell
+make install-service-binding-operator
+```
+
+This makes the `ServiceBindingRequest` custom resource available, that the application developer will use later.
 
 #### Install the DB operator using an `OperatorSource`
 
@@ -242,6 +248,7 @@ The Node.js application should be already up and running after we [imported it b
 Let's check by navigating to the application's route to verify that it is up. Notice that in the header it says `(DB: N/A)`. That means that the application is not connected to DB and so it should not work properly. Try the application's UI to add a fruit - it causes an error proving that the DB is not connected.
 
 Now we ask the Service Binding Operator to bind the DB to the Node.js application in the following step:
+
 * [Create `ServiceBindingRequest`](#create-servicebindingrequest-for-the-nodejs-application)
 
 #### Create `ServiceBindingRequest` for the Node.js application
diff --git a/examples/nodejs_postgresql/README.md b/examples/nodejs_postgresql/README.md
index fae2ae86..247b30ca 100644
--- a/examples/nodejs_postgresql/README.md
+++ b/examples/nodejs_postgresql/README.md
@@ -29,11 +29,17 @@ demonstrate a sample use case.
 
 #### Install the Service Binding Operator
 
-Navigate to the `Operators`->`OperatorHub` in the OpenShift console and in the `Other` category select the `Service Bidning Operator` operator
+Navigate to the `Operators`->`OperatorHub` in the OpenShift console and in the `Developer Tools` category select the `Service Binding Operator` operator
 
 ![Service Binding Operator as shown in OperatorHub](../../assets/operator-hub-sbo-screenshot.png)
 
-and install a `stable` version.
+and install a `alpha` version.
+
+Alternatively, you can perform the same task with this make command:
+
+``` shell
+make install-service-binding-operator
+```
 
 This makes the `ServiceBindingRequest` custom resource available, that the application developer will use later.
 
@@ -201,4 +207,3 @@ spec:
 | Secret | The name of the intermediate secret |
 
 That's it, folks!
-
diff --git a/go.mod b/go.mod
index 416cd39d..295a17bc 100644
--- a/go.mod
+++ b/go.mod
@@ -11,7 +11,7 @@ require (
 	github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible
 	github.com/operator-backing-service-samples/postgresql-operator v0.0.0-20191023140509-5c3697ed3069
 	github.com/operator-framework/operator-lifecycle-manager v0.0.0-20191115003340-16619cd27fa5
-	github.com/operator-framework/operator-sdk v0.15.1
+	github.com/operator-framework/operator-sdk v0.15.2
 	github.com/pmezard/go-difflib v1.0.0
 	github.com/spf13/pflag v1.0.5
 	github.com/stretchr/testify v1.4.0
diff --git a/go.sum b/go.sum
index 4f0b2201..29e59c2a 100644
--- a/go.sum
+++ b/go.sum
@@ -586,8 +586,8 @@ github.com/operator-framework/operator-registry v1.5.1/go.mod h1:agrQlkWOo1q8U1S
 github.com/operator-framework/operator-registry v1.5.3/go.mod h1:agrQlkWOo1q8U1SAaLSS2WQ+Z9vswNT2M2HFib9iuLY=
 github.com/operator-framework/operator-registry v1.5.7-0.20200121213444-d8e2ec52c19a/go.mod h1:ekexcV4O8YMxdQuPb+Xco7MHfVmRIq7Jvj5e6NU7dHI=
 github.com/operator-framework/operator-sdk v0.8.2-0.20190522220659-031d71ef8154/go.mod h1:iVyukRkam5JZa8AnjYf+/G3rk7JI1+M6GsU0sq0B9NA=
-github.com/operator-framework/operator-sdk v0.15.1 h1:MSyez9UD47UtA0voGYotk/8i64Km7fZyfgwGRlhOvqY=
-github.com/operator-framework/operator-sdk v0.15.1/go.mod h1:RkC5LpluVONa08ORFIIVCYrEr855xG1/NltRL2jQ8qo=
+github.com/operator-framework/operator-sdk v0.15.2 h1:VlxI0J+HLmMKs5k53drQ/B1AXhyNj0OaD2BbREt8Hnk=
+github.com/operator-framework/operator-sdk v0.15.2/go.mod h1:RkC5LpluVONa08ORFIIVCYrEr855xG1/NltRL2jQ8qo=
 github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc=
 github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
 github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4=
diff --git a/hack/check-csvs.sh b/hack/check-csvs.sh
index 12a6aa65..1c04855c 100755
--- a/hack/check-csvs.sh
+++ b/hack/check-csvs.sh
@@ -4,11 +4,11 @@ function check_csvs () {
     local csv_name="$1"
 
     for i in  {1..120} ; do
-        if ( kubectl get csvs |grep ${csv_name} 2>&1 > /dev/null ) ; then
+        if ( kubectl get csvs -n default | grep ${csv_name} 2>&1 > /dev/null ) ; then
             return 0
         fi
 
-        sleep 3
+        sleep 10
     done
 
     return 1
diff --git a/hack/dev-release.sh b/hack/dev-release.sh
index bf9b918d..bc214316 100755
--- a/hack/dev-release.sh
+++ b/hack/dev-release.sh
@@ -21,7 +21,7 @@ VERSION_NUMBER=`kubectl get csvs  -n=default -o jsonpath='{.items[*].spec.versio
 if [ "${VERSION_NUMBER}" = "${BUNDLE_VERSION}" ] ; then
     echo -e "OLM Bundle Version validation succeeded \n ";
 	kubectl get csvs -n=default -o jsonpath='{.items[*].metadata.annotations.alm-examples}' | cut -d "[" -f 2 | cut -d "]" -f 1 > output.json;
-	kubectl apply -f ./output.json;
+	kubectl apply -n=default -f ./output.json;
 	if [ $? = 0 ] ; then
 		echo "CSV alm example validation succeeded"
 	fi
diff --git a/hack/examples-commons.sh b/hack/examples-commons.sh
index 179623fb..e3060f4a 100755
--- a/hack/examples-commons.sh
+++ b/hack/examples-commons.sh
@@ -127,34 +127,18 @@ function uninstall_postgresql_db_instance {
 
 ## Service Binding Operator
 
-function install_service_binding_operator_source {
-    OPSRC_NAMESPACE=redhat-developer
-    OPSRC_NAME=redhat-developer-operators
-    PACKAGE_NAME=service-binding-operator
-
-    install_operator_source $OPSRC_NAMESPACE $OPSRC_NAME
-    wait_for_packagemanifest $PACKAGE_NAME
-}
-
-function uninstall_service_binding_operator_source {
-    OPSRC_NAMESPACE=redhat-developer
-    OPSRC_NAME=redhat-developer-operators
-
-    uninstall_operator_source $OPSRC_NAMESPACE $OPSRC_NAME
-}
-
 function install_service_binding_operator_subscription {
     NAME=service-binding-operator
-    OPSRC_NAME=redhat-developer-operators
-    CHANNEL=stable
+    OPSRC_NAME=community-operators
+    CHANNEL=alpha
 
     install_operator_subscription $NAME $OPSRC_NAME $CHANNEL
 }
 
 function uninstall_service_binding_operator_subscription {
     NAME=service-binding-operator
-    OPSRC_NAME=redhat-developer-operators
-    CHANNEL=stable
+    OPSRC_NAME=community-operators
+    CHANNEL=alpha
 
     uninstall_operator_subscription $NAME $OPSRC_NAME $CHANNEL
     uninstall_current_csv $NAME $CHANNEL
diff --git a/openshift-ci/Dockerfile.tools b/openshift-ci/Dockerfile.tools
index fdbee19b..ab2a7ea6 100644
--- a/openshift-ci/Dockerfile.tools
+++ b/openshift-ci/Dockerfile.tools
@@ -31,7 +31,7 @@ RUN mkdir -p /tmp/goroot
 RUN curl -Lo go1.13.7.linux-amd64.tar.gz https://dl.google.com/go/go1.13.7.linux-amd64.tar.gz && tar -C /tmp/goroot -xzf go1.13.7.linux-amd64.tar.gz
 RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl && chmod +x kubectl && mv kubectl $GOPATH/bin/
 
-RUN curl -Lo operator-sdk https://github.com/operator-framework/operator-sdk/releases/download/v0.15.1/operator-sdk-v0.15.1-x86_64-linux-gnu && chmod +x operator-sdk && mv operator-sdk $GOPATH/bin/
+RUN curl -Lo operator-sdk https://github.com/operator-framework/operator-sdk/releases/download/v0.15.2/operator-sdk-v0.15.2-x86_64-linux-gnu && chmod +x operator-sdk && mv operator-sdk $GOPATH/bin/
 
 RUN mkdir -p ${GOPATH}/src/${GO_PACKAGE_PATH}/
 
diff --git a/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go b/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go
index 45ede407..b0fbdd72 100644
--- a/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go
+++ b/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go
@@ -27,10 +27,11 @@ type ServiceBindingRequestSpec struct {
 	CustomEnvVar []corev1.EnvVar `json:"customEnvVar,omitempty"`
 
 	// BackingServiceSelector is used to identify the backing service operator.
-	BackingServiceSelector BackingServiceSelector `json:"backingServiceSelector"`
+	// +optional
+	BackingServiceSelector BackingServiceSelector `json:"backingServiceSelector,omitempty"`
 
 	// BackingServiceSelectors is used to identify multiple backing services.
-	BackingServiceSelectors []BackingServiceSelector `json:"backingServiceSelectors"`
+	BackingServiceSelectors []BackingServiceSelector `json:"backingServiceSelectors,omitempty"`
 
 	// ApplicationSelector is used to identify the application connecting to the
 	// backing service operator.
diff --git a/pkg/controller/servicebindingrequest/binder.go b/pkg/controller/servicebindingrequest/binder.go
index 8004a8f8..3543555d 100644
--- a/pkg/controller/servicebindingrequest/binder.go
+++ b/pkg/controller/servicebindingrequest/binder.go
@@ -421,20 +421,21 @@ func nestedMapComparison(a, b *unstructured.Unstructured, fields ...string) (boo
 	var (
 		aMap map[string]interface{}
 		bMap map[string]interface{}
-		ok   bool
+		aOk  bool
+		bOk  bool
 		err  error
 	)
 
-	if aMap, ok, err = unstructured.NestedMap(a.Object, fields...); err != nil {
+	if aMap, aOk, err = unstructured.NestedMap(a.Object, fields...); err != nil {
 		return false, err
-	} else if !ok {
-		return false, fmt.Errorf("original object doesn't have a 'spec' field")
 	}
 
-	if bMap, ok, err = unstructured.NestedMap(b.Object, fields...); err != nil {
+	if bMap, bOk, err = unstructured.NestedMap(b.Object, fields...); err != nil {
 		return false, err
-	} else if !ok {
-		return false, fmt.Errorf("original object doesn't have a 'spec' field")
+	}
+
+	if aOk != bOk {
+		return false, nil
 	}
 
 	result := cmp.DeepEqual(aMap, bMap)()
diff --git a/pkg/controller/servicebindingrequest/binding.go b/pkg/controller/servicebindingrequest/binding.go
index 1cfea17c..81a7cd75 100644
--- a/pkg/controller/servicebindingrequest/binding.go
+++ b/pkg/controller/servicebindingrequest/binding.go
@@ -186,7 +186,7 @@ func (b *ServiceBinder) Bind() (reconcile.Result, error) {
 	sbrStatus := b.SBR.Status.DeepCopy()
 
 	b.Logger.Info("Saving data on intermediary secret...")
-	secretObj, err := b.Secret.Commit(b.Data, nil)
+	secretObj, err := b.Secret.Commit(b.Data)
 	if err != nil {
 		b.Logger.Error(err, "On saving secret data..")
 		return b.onError(err, b.SBR, sbrStatus, nil)
@@ -208,7 +208,7 @@ func (b *ServiceBinder) Bind() (reconcile.Result, error) {
 
 	// annotating objects related to binding
 	namespacedName := types.NamespacedName{Namespace: b.SBR.GetNamespace(), Name: b.SBR.GetName()}
-	if err = SetSBRAnnotations(b.DynClient, namespacedName, b.Objects); err != nil {
+	if err = SetSBRAnnotations(b.DynClient, namespacedName, append(b.Objects, secretObj)); err != nil {
 		b.Logger.Error(err, "On setting annotations in related objects.")
 		return b.onError(err, b.SBR, sbrStatus, updatedObjects)
 	}
@@ -220,7 +220,7 @@ func (b *ServiceBinder) Bind() (reconcile.Result, error) {
 	}
 
 	// appending finalizer, should be later removed upon resource deletion
-	sbr.SetFinalizers(append(sbr.GetFinalizers(), Finalizer))
+	sbr.SetFinalizers(append(removeStringSlice(b.SBR.GetFinalizers(), Finalizer), Finalizer))
 	if _, err = b.updateServiceBindingRequest(sbr); err != nil {
 		return NoRequeue(err)
 	}
@@ -302,17 +302,10 @@ func BuildServiceBinder(options *ServiceBinderOptions) (*ServiceBinder, error) {
 
 	// gather related secret, again only appending it if there's a value.
 	secret := NewSecret(options.DynClient, plan)
-	secretObj, found, err := secret.Get()
-	if err != nil {
-		return nil, err
-	}
-	if found {
-		objs = append(objs, secretObj)
-	}
 
 	return &ServiceBinder{
 		Logger:    options.Logger,
-		Binder:    NewBinder(ctx, options.Client, options.DynClient, options.SBR, retriever.volumeKeys),
+		Binder:    NewBinder(ctx, options.Client, options.DynClient, options.SBR, retriever.VolumeKeys),
 		DynClient: options.DynClient,
 		SBR:       options.SBR,
 		Objects:   objs,
diff --git a/pkg/controller/servicebindingrequest/olm.go b/pkg/controller/servicebindingrequest/olm.go
index e226ed51..c48ab3f2 100644
--- a/pkg/controller/servicebindingrequest/olm.go
+++ b/pkg/controller/servicebindingrequest/olm.go
@@ -1,6 +1,7 @@
 package servicebindingrequest
 
 import (
+	"fmt"
 	"sort"
 	"strings"
 
@@ -114,9 +115,8 @@ func (o *OLM) loopCRDDescriptions(
 	return nil
 }
 
-// SelectCRDDescriptionByGVK return a single CRDDescription filled with information from CSVs and
-// annotations.
-func (o *OLM) SelectCRDDescriptionByGVK(gvk schema.GroupVersionKind, crd *unstructured.Unstructured) (*olmv1alpha1.CRDDescription, error) {
+// SelectCRDByGVK return a single CRD based on a given GVK.
+func (o *OLM) SelectCRDByGVK(gvk schema.GroupVersionKind, crd *unstructured.Unstructured) (*olmv1alpha1.CRDDescription, error) {
 	log := o.logger.WithValues("Selector.GVK", gvk)
 	ownedCRDs, err := o.ListCSVOwnedCRDs()
 	if err != nil {
@@ -124,17 +124,19 @@ func (o *OLM) SelectCRDDescriptionByGVK(gvk schema.GroupVersionKind, crd *unstru
 		return nil, err
 	}
 
-	var resultCRDDescription *olmv1alpha1.CRDDescription
+	var crdDescription *olmv1alpha1.CRDDescription
 
 	// CRDDescription is both used by OLM to configure OLM descriptors in manifests existing in the
 	// cluster but is also built from annotations present in the CRD
 	if crd != nil {
-		resultCRDDescription, err = buildCRDDescriptionFromCRD(crd)
+		crdDescription, err = buildCRDDescriptionFromCRD(crd)
 		if err != nil {
 			return nil, err
 		}
 	}
 
+	crdDescriptions := []*olmv1alpha1.CRDDescription{}
+
 	err = o.loopCRDDescriptions(ownedCRDs, func(crdDescription *olmv1alpha1.CRDDescription) {
 		log = o.logger.WithValues(
 			"CRDDescription.Name", crdDescription.Name,
@@ -152,18 +154,21 @@ func (o *OLM) SelectCRDDescriptionByGVK(gvk schema.GroupVersionKind, crd *unstru
 			return
 		}
 		log.Debug("CRDDescription object matches selector!")
-		if resultCRDDescription == nil {
-			resultCRDDescription = crdDescription.DeepCopy()
-		}
-
-		resultCRDDescription.StatusDescriptors = append(resultCRDDescription.StatusDescriptors, crdDescription.StatusDescriptors...)
-		resultCRDDescription.SpecDescriptors = append(resultCRDDescription.SpecDescriptors, crdDescription.SpecDescriptors...)
+		crdDescriptions = append(crdDescriptions, crdDescription)
 	})
 	if err != nil {
 		return nil, err
 	}
 
-	return resultCRDDescription, nil
+	if len(crdDescriptions) == 0 && crdDescription == nil {
+		log.Debug("No CRD could be found for GVK.")
+		return nil, fmt.Errorf("no crd could be found for gvk")
+	} else if len(crdDescriptions) == 0 {
+		// use the crdDescription built from CRD annotations as fallback
+		return crdDescription, nil
+	}
+
+	return crdDescriptions[0], nil
 }
 
 // buildCRDDescriptionFromCRD builds a CRDDescription from annotations present in the CRD.
diff --git a/pkg/controller/servicebindingrequest/plan_decoupled.go b/pkg/controller/servicebindingrequest/plan_decoupled.go
index ce0bfb25..9252cfaa 100644
--- a/pkg/controller/servicebindingrequest/plan_decoupled.go
+++ b/pkg/controller/servicebindingrequest/plan_decoupled.go
@@ -4,12 +4,10 @@ import "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 
 // GetCRs returns a slice of unstructured CRs contained in the internal related resources collection.
 func (p *Plan) GetCRs() []*unstructured.Unstructured {
-	// return p.RelatedResources.GetCRs()
-	panic("implement me")
+	return p.RelatedResources.GetCRs()
 }
 
 // GetRelatedResources returns the collection of related resources enumerated in the plan.
 func (p *Plan) GetRelatedResources() RelatedResources {
-	//return p.RelatedResources
-	panic("implement me")
+	return p.RelatedResources
 }
diff --git a/pkg/controller/servicebindingrequest/planner.go b/pkg/controller/servicebindingrequest/planner.go
index 70e6bb50..6a4ef392 100644
--- a/pkg/controller/servicebindingrequest/planner.go
+++ b/pkg/controller/servicebindingrequest/planner.go
@@ -2,9 +2,7 @@ package servicebindingrequest
 
 import (
 	"context"
-	"strings"
 
-	olmv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
 	"k8s.io/apimachinery/pkg/api/meta"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -30,86 +28,87 @@ type Planner struct {
 
 // Plan outcome, after executing planner.
 type Plan struct {
-	Ns             string                         // namespace name
-	Name           string                         // plan name, same than ServiceBindingRequest
-	CRDDescription *olmv1alpha1.CRDDescription    // custom resource definition description
-	CR             *unstructured.Unstructured     // custom resource object
-	SBR            v1alpha1.ServiceBindingRequest // service binding request
+	Ns               string                         // namespace name
+	Name             string                         // plan name, same than ServiceBindingRequest
+	SBR              v1alpha1.ServiceBindingRequest // service binding request
+	RelatedResources RelatedResources               // CR and CRDDescription pairs SBR related
 }
 
 // searchCR based on a CustomResourceDefinitionDescription and name, search for the object.
-func (p *Planner) searchCR() (*unstructured.Unstructured, error) {
-	bss := p.sbr.Spec.BackingServiceSelector
-	gvk := schema.GroupVersionKind{Group: bss.Group, Version: bss.Version, Kind: bss.Kind}
-	gvr, _ := meta.UnsafeGuessKindToResource(gvk)
-	opts := metav1.GetOptions{}
-
-	log := p.logger.WithValues("CR.GVK", gvk.String(), "CR.GVR", gvr.String())
-	log.Debug("Searching for CR instance...")
-
-	cr, err := p.client.Resource(gvr).Namespace(p.sbr.GetNamespace()).Get(bss.ResourceRef, opts)
-
-	if err != nil {
-		log.Info("during reading CR")
-		return nil, err
+func (p *Planner) searchCR(namespace string, selector v1alpha1.BackingServiceSelector) (*unstructured.Unstructured, error) {
+	// gvr is the plural guessed resource for the given selector
+	gvk := schema.GroupVersionKind{
+		Group:   selector.Group,
+		Version: selector.Version,
+		Kind:    selector.Kind,
 	}
+	gvr, _ := meta.UnsafeGuessKindToResource(gvk)
+	// delegate the search selector's namespaced resource client
+	return p.client.Resource(gvr).Namespace(namespace).Get(selector.ResourceRef, metav1.GetOptions{})
+}
 
-	log.Debug("Found target CR!", "CR.Name", cr.GetName())
-	return cr, nil
+// CRDGVR is the plural GVR for Kubernetes CRDs.
+var CRDGVR = schema.GroupVersionResource{
+	Group:    "apiextensions.k8s.io",
+	Version:  "v1beta1",
+	Resource: "customresourcedefinitions",
 }
 
-// searchCRD based on a CustomResourceDefinitionDescription and name, search for the object.
-func (p *Planner) searchCRD() (*unstructured.Unstructured, error) {
-	bss := p.sbr.Spec.BackingServiceSelector
-	gvk := schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1beta1", Kind: "CustomResourceDefinition"}
+// searchCRD returns the CRD related to the gvk.
+func (p *Planner) searchCRD(gvk schema.GroupVersionKind) (*unstructured.Unstructured, error) {
+	// gvr is the plural guessed resource for the given GVK
 	gvr, _ := meta.UnsafeGuessKindToResource(gvk)
-	opts := metav1.GetOptions{}
-
-	logger := p.logger.WithValues("CR.GVK", gvk.String(), "CR.GVR", gvr.String(), "Kind", bss.Kind)
-	logger.Info("Searching for CRD instance...")
-
-	// TODO: This hack should be removed! Probably the name should be prompted from user through SBR CR.
-	name := strings.ToLower(bss.Kind) + "s." + bss.Group
-	crd, err := p.client.Resource(gvr).Get(name, opts)
-
-	if err != nil {
-		logger.Info("during reading CRD")
-		return nil, err
-	}
-
-	logger.WithValues("CR.Name", crd.GetName()).Info("Found target CR!")
-	return crd, nil
+	// crdName is the string'fied GroupResource, e.g. "deployments.apps"
+	crdName := gvr.GroupResource().String()
+	// delegate the search to the CustomResourceDefinition resource client
+	return p.client.Resource(CRDGVR).Get(crdName, metav1.GetOptions{})
 }
 
 // Plan by retrieving the necessary resources related to binding a service backend.
 func (p *Planner) Plan() (*Plan, error) {
-	bss := p.sbr.Spec.BackingServiceSelector
-	gvk := schema.GroupVersionKind{Group: bss.Group, Version: bss.Version, Kind: bss.Kind}
-	olm := NewOLM(p.client, p.sbr.GetNamespace())
-	crd, err := p.searchCRD()
-	if err != nil {
-		return nil, err
-	}
-
-	p.logger.Debug("After search crd", "CRD", crd)
-
-	crdDescription, err := olm.SelectCRDDescriptionByGVK(gvk, crd)
-	if err != nil {
-		return nil, err
+	ns := p.sbr.GetNamespace()
+	selectors := append([]v1alpha1.BackingServiceSelector{}, p.sbr.Spec.BackingServiceSelectors...)
+	if (p.sbr.Spec.BackingServiceSelector != v1alpha1.BackingServiceSelector{}) {
+		selectors = append(selectors, p.sbr.Spec.BackingServiceSelector)
 	}
 
-	// retrieve the CR based on kind, api-version and name
-	cr, err := p.searchCR()
-	if err != nil {
-		return nil, err
+	relatedResources := make([]*RelatedResource, 0)
+	for _, s := range selectors {
+		bssGVK := schema.GroupVersionKind{Kind: s.Kind, Version: s.Version, Group: s.Group}
+
+		// resolve the CRD using the service's GVK
+		crd, err := p.searchCRD(bssGVK)
+		if err != nil {
+			return nil, err
+		}
+		p.logger.Debug("Resolved CRD", "CRD", crd)
+
+		// resolve the CRDDescription based on the service's GVK and the resolved CRD
+		olm := NewOLM(p.client, ns)
+		crdDescription, err := olm.SelectCRDByGVK(bssGVK, crd)
+		if err != nil {
+			return nil, err
+		}
+		p.logger.Debug("Resolved CRDDescription", "CRDDescription", crdDescription)
+
+		cr, err := p.searchCR(ns, s)
+		if err != nil {
+			return nil, err
+		}
+
+		r := &RelatedResource{
+			CRDDescription: crdDescription,
+			CR:             cr,
+		}
+		relatedResources = append(relatedResources, r)
+		p.logger.Debug("Resolved related resource", "RelatedResource", r)
 	}
 
 	return &Plan{
-		Ns:             p.sbr.GetNamespace(),
-		Name:           p.sbr.GetName(),
-		CRDDescription: crdDescription,
-		CR:             cr,
-		SBR:            *p.sbr,
+		Name:             p.sbr.GetName(),
+		Ns:               ns,
+		RelatedResources: relatedResources,
+		SBR:              *p.sbr,
 	}, nil
 }
 
diff --git a/pkg/controller/servicebindingrequest/reconciler.go b/pkg/controller/servicebindingrequest/reconciler.go
index 88ebe6c4..431dd4a5 100644
--- a/pkg/controller/servicebindingrequest/reconciler.go
+++ b/pkg/controller/servicebindingrequest/reconciler.go
@@ -1,13 +1,7 @@
 package servicebindingrequest
 
 import (
-	"context"
-	"errors"
-	"fmt"
-
-	"gotest.tools/assert/cmp"
 	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"
 	"k8s.io/client-go/dynamic"
@@ -15,7 +9,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1"
-	"github.com/redhat-developer/service-binding-operator/pkg/converter"
 	"github.com/redhat-developer/service-binding-operator/pkg/log"
 )
 
@@ -27,202 +20,58 @@ type Reconciler struct {
 }
 
 const (
-	// BindingSuccess binding has succeeded
 	BindingSuccess = "Success"
-	// sbrFinalizer annotation used in finalizer steps
-	sbrFinalizer = "finalizer.servicebindingrequest.openshift.io"
 )
 
 // reconcilerLog local logger instance
 var reconcilerLog = log.NewLog("reconciler")
 
-// setSecretName update the CR status field to "in progress", and setting secret name.
-func (r *Reconciler) setSecretName(sbrStatus *v1alpha1.ServiceBindingRequestStatus, name string) {
-	sbrStatus.BindingStatus = bindingInProgress
-	sbrStatus.Secret = name
-}
-
-// setStatus update the CR status field.
-func (r *Reconciler) setStatus(sbrStatus *v1alpha1.ServiceBindingRequestStatus, status string) {
-	sbrStatus.BindingStatus = status
-}
-
-// setApplicationObjects set the ApplicationObject status field, and also set the overall status as
-// success, since it was able to bind applications.
-func (r *Reconciler) setApplicationObjects(
-	sbrStatus *v1alpha1.ServiceBindingRequestStatus,
-	objs []*unstructured.Unstructured,
-) {
-	names := []string{}
-	for _, obj := range objs {
-		names = append(names, fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName()))
-	}
-	sbrStatus.BindingStatus = BindingSuccess
-	sbrStatus.ApplicationObjects = names
-}
+//// validateServiceBindingRequest check for unsupported settings in SBR.
+//func (r *Reconciler) validateServiceBindingRequest(sbr *v1alpha1.ServiceBindingRequest) error {
+//	// check if application ResourceRef and MatchLabels, one of them is required.
+//	if sbr.Spec.ApplicationSelector.ResourceRef == "" &&
+//		sbr.Spec.ApplicationSelector.LabelSelector == nil {
+//		return fmt.Errorf("both ResourceRef and LabelSelector are not set")
+//	}
+//	return nil
+//}
 
 // getServiceBindingRequest retrieve the SBR object based on namespaced-name.
 func (r *Reconciler) getServiceBindingRequest(
 	namespacedName types.NamespacedName,
 ) (*v1alpha1.ServiceBindingRequest, error) {
-	gvr := v1alpha1.SchemeGroupVersion.WithResource(ServiceBindingRequestResource)
-	resourceClient := r.dynClient.Resource(gvr).Namespace(namespacedName.Namespace)
+	gr := v1alpha1.SchemeGroupVersion.WithResource(ServiceBindingRequestResource)
+	resourceClient := r.dynClient.Resource(gr).Namespace(namespacedName.Namespace)
 	u, err := resourceClient.Get(namespacedName.Name, metav1.GetOptions{})
 	if err != nil {
 		return nil, err
 	}
-	sbr := &v1alpha1.ServiceBindingRequest{}
-	err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, sbr)
-	if err != nil {
-		return nil, err
-	}
-	return sbr, nil
-}
-
-// updateStatusServiceBindingRequest update the status field of a ServiceBindingRequest.
-func (r *Reconciler) updateStatusServiceBindingRequest(
-	sbr *v1alpha1.ServiceBindingRequest,
-	sbrStatus *v1alpha1.ServiceBindingRequestStatus,
-) (*v1alpha1.ServiceBindingRequest, error) {
-	// do not update if both statuses are equal
-	if result := cmp.DeepEqual(sbr.Status, sbrStatus)(); result.Success() {
-		return sbr, nil
-	}
-
-	// coping status over informed object
-	sbr.Status = *sbrStatus
-
-	// converting object into unstructured
-	u, err := converter.ToUnstructured(sbr)
-	if err != nil {
-		return nil, err
-	}
-
-	gvr := v1alpha1.SchemeGroupVersion.WithResource(ServiceBindingRequestResource)
-	resourceClient := r.dynClient.Resource(gvr).Namespace(sbr.GetNamespace())
-	u, err = resourceClient.UpdateStatus(u, metav1.UpdateOptions{})
-	if err != nil {
-		return nil, err
-	}
 
+	sbr := &v1alpha1.ServiceBindingRequest{}
 	err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, sbr)
 	if err != nil {
 		return nil, err
 	}
-	return sbr, nil
-}
-
-// updateServiceBindingRequest execute update API call on a SBR request. It can return errors from
-// this action.
-func (r *Reconciler) updateServiceBindingRequest(
-	sbr *v1alpha1.ServiceBindingRequest,
-) (*v1alpha1.ServiceBindingRequest, error) {
-	u, err := converter.ToUnstructured(sbr)
-	if err != nil {
-		return nil, err
-	}
-	gvr := v1alpha1.SchemeGroupVersion.WithResource(ServiceBindingRequestResource)
-	resourceClient := r.dynClient.Resource(gvr).Namespace(sbr.GetNamespace())
-	u, err = resourceClient.Update(u, metav1.UpdateOptions{})
-	if err != nil {
-		return nil, err
-	}
 
-	err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, sbr)
-	if err != nil {
-		return nil, err
-	}
 	return sbr, nil
 }
 
-// onError comprise the update of ServiceBindingRequest status to set error flag, and inspect
-// informed error to apply a different behavior for not-founds.
-func (r *Reconciler) onError(
-	err error,
-	sbr *v1alpha1.ServiceBindingRequest,
-	sbrStatus *v1alpha1.ServiceBindingRequestStatus,
-	objs []*unstructured.Unstructured,
-) (reconcile.Result, error) {
-	// settting overall status to failed
-	r.setStatus(sbrStatus, bindingFail)
-	//
-	if objs != nil {
-		r.setApplicationObjects(sbrStatus, objs)
-	}
-	_, errStatus := r.updateStatusServiceBindingRequest(sbr, sbrStatus)
-	if errStatus != nil {
-		return RequeueError(errStatus)
-	}
-	return RequeueOnNotFound(err, requeueAfter)
-}
-
-// checkSBR checks the Service Binding Request
-func checkSBR(sbr *v1alpha1.ServiceBindingRequest, log *log.Log) error {
-	// Check if application ResourceRef is present
-	if sbr.Spec.ApplicationSelector.ResourceRef == "" {
-		log.Debug("Spec.ApplicationSelector.ResourceRef not found")
-
-		// Check if MatchLabels is present
-		if sbr.Spec.ApplicationSelector.LabelSelector == nil {
-			err := errors.New("NotFoundError")
-			log.Error(err, "Spec.ApplicationSelector.LabelSelector not found")
-			return err
-		}
-	}
-	return nil
-}
-
 // unbind removes the relationship between the given sbr and the manifests the operator has
 // previously modified. This process also deletes any manifests created to support the binding
 // functionality, such as ConfigMaps and Secrets.
-func (r *Reconciler) unbind(
-	logger *log.Log,
-	binder *Binder,
-	secret *Secret,
-	sbr *v1alpha1.ServiceBindingRequest,
-	objectsToAnnotate []*unstructured.Unstructured,
-) (reconcile.Result, error) {
+func (r *Reconciler) unbind(logger *log.Log, bm *ServiceBinder) (reconcile.Result, error) {
 	logger = logger.WithName("unbind")
 
 	// when finalizer is not found anymore, it can be safely removed
-	if !containsStringSlice(sbr.GetFinalizers(), sbrFinalizer) {
+	if !containsStringSlice(bm.SBR.GetFinalizers(), Finalizer) {
 		logger.Info("Resource can be safely deleted!")
 		return Done()
 	}
 
-	logger.Debug("Reading intermediary secret before deletion.")
-	secretObj, _, err := secret.Get()
-	if err != nil {
-		logger.Error(err, "On reading intermediary secret.")
-		return RequeueError(err)
-	}
-
-	// adding secret on list of objects, to remove annotations from secret before deletion
-	objectsToAnnotate = append(objectsToAnnotate, secretObj)
-
-	logger.Info("Cleaning related objects from operator's annotations...")
-	if err = RemoveSBRAnnotations(r.dynClient, objectsToAnnotate); err != nil {
-		logger.Error(err, "On removing annotations from related objects.")
-		return RequeueError(err)
-	}
-
 	logger.Info("Executing unbinding steps...")
-	err = binder.Unbind()
-	if err != nil {
+	if res, err := bm.Unbind(); err != nil {
 		logger.Error(err, "On unbinding application.")
-		return RequeueError(err)
-	}
-
-	logger.Info("Deleting intermediary secret...")
-	if err = secret.Delete(); err != nil {
-		logger.Error(err, "On deleting intermediary secret.")
-		return RequeueError(err)
-	}
-
-	logger.Debug("Removing resource finalizers...")
-	sbr.SetFinalizers(removeStringSlice(sbr.GetFinalizers(), sbrFinalizer))
-	if _, err = r.updateServiceBindingRequest(sbr); err != nil {
-		return NoRequeue(err)
+		return res, err
 	}
 
 	logger.Debug("Deletion done!")
@@ -233,60 +82,16 @@ func (r *Reconciler) unbind(
 // in the common parts of the reconciler, and execute the final binding steps.
 func (r *Reconciler) bind(
 	logger *log.Log,
-	binder *Binder,
-	secret *Secret,
-	retrievedData map[string][]byte,
-	retrievedCache map[string]interface{},
-	sbr *v1alpha1.ServiceBindingRequest,
+	bm *ServiceBinder,
 	sbrStatus *v1alpha1.ServiceBindingRequestStatus,
-	objectsToAnnotate []*unstructured.Unstructured,
-) (reconcile.Result, error) {
+) (
+	reconcile.Result,
+	error,
+) {
 	logger = logger.WithName("bind")
 
-	logger.Info("Saving data on intermediary secret...")
-	secretObj, err := secret.Commit(retrievedData, retrievedCache)
-	if err != nil {
-		logger.Error(err, "On saving secret data..")
-		return r.onError(err, sbr, sbrStatus, nil)
-	}
-
-	// appending intermediary secret in the list of objects to annotate
-	objectsToAnnotate = append(objectsToAnnotate, secretObj)
-	// making sure secret name is part of status
-	r.setSecretName(sbrStatus, secretObj.GetName())
-
 	logger.Info("Binding applications with intermediary secret...")
-	updatedObjects, err := binder.Bind()
-	if err != nil {
-		logger.Error(err, "On binding application.")
-		return r.onError(err, sbr, sbrStatus, updatedObjects)
-	}
-
-	// saving on status the list of objects that have been touched
-	r.setApplicationObjects(sbrStatus, updatedObjects)
-	namespacedName := types.NamespacedName{Namespace: sbr.GetNamespace(), Name: sbr.GetName()}
-
-	// annotating objects related to binding
-	if err = SetSBRAnnotations(r.dynClient, namespacedName, objectsToAnnotate); err != nil {
-		logger.Error(err, "On setting annotations in related objects.")
-		return r.onError(err, sbr, sbrStatus, updatedObjects)
-	}
-
-	// updating status of request instance
-	if sbr, err = r.updateStatusServiceBindingRequest(sbr, sbrStatus); err != nil {
-		return RequeueOnConflict(err)
-	}
-
-	// appending finalizer, should be later removed upon resource deletion
-	if !containsStringSlice(sbr.GetFinalizers(), sbrFinalizer) {
-		sbr.SetFinalizers(append(sbr.GetFinalizers(), sbrFinalizer))
-		if _, err = r.updateServiceBindingRequest(sbr); err != nil {
-			return NoRequeue(err)
-		}
-	}
-
-	logger.Info("All done!")
-	return Done()
+	return bm.Bind()
 }
 
 // Reconcile a ServiceBindingRequest by the following steps:
@@ -300,9 +105,6 @@ func (r *Reconciler) bind(
 // 4. Search applications that are interested to bind with given service, by inspecting labels. The
 //    Deployment (and other kinds) will be updated in "spec" level.
 func (r *Reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) {
-	ctx := context.TODO()
-	objectsToAnnotate := []*unstructured.Unstructured{}
-
 	logger := reconcilerLog.WithValues(
 		"Request.Namespace", request.Namespace,
 		"Request.Name", request.Name,
@@ -310,66 +112,47 @@ func (r *Reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err
 
 	logger.Info("Reconciling ServiceBindingRequest...")
 
-	// fetch the ServiceBindingRequest instance
+	// fetch and validate namespaced ServiceBindingRequest instance
 	sbr, err := r.getServiceBindingRequest(request.NamespacedName)
 	if err != nil {
 		logger.Error(err, "On retrieving service-binding-request instance.")
 		return DoneOnNotFound(err)
 	}
 
+	// validate namespaced ServiceBindingRequest instance (this check has been disabled until test data has been
+	// adjusted to reflect the validation)
+	//
+	//if err = r.validateServiceBindingRequest(sbr); err != nil {
+	//	logger.Error(err, "On validating service-binding-request instance.")
+	//	return Done()
+	//}
+
 	logger = logger.WithValues("ServiceBindingRequest.Name", sbr.Name)
 	logger.Debug("Found service binding request to inspect")
 
 	// splitting instance from it's status
 	sbrStatus := &sbr.Status
 
-	// check Service Binding Request
-	if err = checkSBR(sbr, logger); err != nil {
-		return RequeueError(err)
-	}
-
-	//
-	// Planing changes
-	//
-
-	logger.Debug("Creating a plan based on OLM and CRD.")
-	planner := NewPlanner(ctx, r.dynClient, sbr)
-	plan, err := planner.Plan()
-	if err != nil {
-		logger.Error(err, "On creating a plan to bind applications.")
-		return r.onError(err, sbr, sbrStatus, nil)
+	options := &ServiceBinderOptions{
+		Client:                 r.client,
+		DynClient:              r.dynClient,
+		DetectBindingResources: sbr.Spec.DetectBindingResources,
+		EnvVarPrefix:           sbr.Spec.EnvVarPrefix,
+		SBR:                    sbr,
+		Logger:                 logger,
 	}
 
-	// storing CR in objects to annotate
-	objectsToAnnotate = append(objectsToAnnotate, plan.CR)
-
-	//
-	// Retrieving data
-	//
-
-	logger.Debug("Retrieving data to create intermediate secret.")
-	retriever := NewRetriever(r.dynClient, plan, sbr.Spec.EnvVarPrefix)
-	retrievedData, retrievedCache, err := retriever.Retrieve()
+	bm, err := BuildServiceBinder(options)
 	if err != nil {
-		logger.Error(err, "On retrieving binding data.")
-		return r.onError(err, sbr, sbrStatus, nil)
+		logger.Error(err, "Creating binding context")
+		return RequeueError(err)
 	}
 
-	// storing objects used in Retriever
-	objectsToAnnotate = append(objectsToAnnotate, retriever.Objects...)
-
-	//
-	// Binding and unbind intermediary secret
-	//
-
-	secret := NewSecret(r.dynClient, plan)
-	binder := NewBinder(ctx, r.client, r.dynClient, sbr, retriever.volumeKeys)
-
 	if sbr.GetDeletionTimestamp() != nil {
 		logger.Info("Resource is marked for deletion...")
-		return r.unbind(logger, binder, secret, sbr, objectsToAnnotate)
+		return r.unbind(logger, bm)
 	}
 
 	logger.Info("Starting the bind of application(s) with backing service...")
-	return r.bind(logger, binder, secret, retrievedData, retrievedCache, sbr, sbrStatus, objectsToAnnotate)
+	return r.bind(logger, bm, sbrStatus)
 }
diff --git a/pkg/controller/servicebindingrequest/retrieve_decoupled.go b/pkg/controller/servicebindingrequest/retrieve_decoupled.go
index 5d68949b..f40c4bcf 100644
--- a/pkg/controller/servicebindingrequest/retrieve_decoupled.go
+++ b/pkg/controller/servicebindingrequest/retrieve_decoupled.go
@@ -42,13 +42,14 @@ func (r *Retriever) ReadBindableResourcesData(
 }
 
 func (r *Retriever) storeInto(cr *unstructured.Unstructured, key string, value []byte) {
-	// r.store(cr, k, v)
-	panic("implement me")
+	r.store(cr, key, value)
 }
 
 func (r *Retriever) copyFrom(u *unstructured.Unstructured, path string, fieldPath string, descriptors []string) error {
-	// r.read(u, "spec", specDescriptor.Path, specDescriptor.XDescriptors); err != nil {
-	panic("implement me")
+	if err := r.read(u, path, fieldPath, descriptors); err != nil {
+		return err
+	}
+	return nil
 }
 
 // ReadCRDDescriptionData reads data related to given crdDescription
diff --git a/pkg/controller/servicebindingrequest/retriever.go b/pkg/controller/servicebindingrequest/retriever.go
index dafc0876..1fae4d90 100644
--- a/pkg/controller/servicebindingrequest/retriever.go
+++ b/pkg/controller/servicebindingrequest/retriever.go
@@ -20,7 +20,7 @@ type Retriever struct {
 	Objects       []*unstructured.Unstructured // list of objects employed
 	client        dynamic.Interface            // Kubernetes API client
 	plan          *Plan                        // plan instance
-	volumeKeys    []string                     // list of keys found
+	VolumeKeys    []string                     // list of keys found
 	bindingPrefix string                       // prefix for variable names
 	cache         map[string]interface{}       // store visited paths
 }
@@ -51,9 +51,9 @@ func (r *Retriever) getNestedValue(key string, sectionMap interface{}) (string,
 }
 
 // getCRKey retrieve key in section from CR object, part of the "plan" instance.
-func (r *Retriever) getCRKey(section string, key string) (string, interface{}, error) {
-	obj := r.plan.CR.Object
-	objName := r.plan.CR.GetName()
+func (r *Retriever) getCRKey(u *unstructured.Unstructured, section string, key string) (string, interface{}, error) {
+	obj := u.Object
+	objName := u.GetName()
 	log := r.logger.WithValues("CR.Name", objName, "CR.section", section, "CR.key", key)
 	log.Debug("Reading CR attributes...")
 
@@ -76,7 +76,7 @@ func (r *Retriever) getCRKey(section string, key string) (string, interface{}, e
 // 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(place, path string, xDescriptors []string) error {
+func (r *Retriever) read(cr *unstructured.Unstructured, place, path string, xDescriptors []string) error {
 	log := r.logger.WithValues(
 		"CR.Section", place,
 		"CRDDescription.Path", path,
@@ -89,13 +89,13 @@ func (r *Retriever) read(place, path string, xDescriptors []string) error {
 
 	// holds the configMap name and items
 	configMaps := make(map[string][]string)
-	pathValue, _, err := r.getCRKey(place, path)
+	pathValue, _, err := r.getCRKey(cr, place, path)
+	if err != nil {
+		return err
+	}
 	for _, xDescriptor := range xDescriptors {
 		log = log.WithValues("CRDDescription.xDescriptor", xDescriptor, "cache", r.cache)
 		log.Debug("Inspecting xDescriptor...")
-		if err != nil {
-			return err
-		}
 
 		if _, ok := r.cache[place].(map[string]interface{}); !ok {
 			r.cache[place] = make(map[string]interface{})
@@ -112,9 +112,9 @@ func (r *Retriever) read(place, path string, xDescriptors []string) error {
 		} else if strings.HasPrefix(xDescriptor, volumeMountSecretPrefix) {
 			secrets[pathValue] = append(secrets[pathValue], r.extractSecretItemName(xDescriptor))
 			r.markVisitedPaths(r.extractSecretItemName(xDescriptor), pathValue, place)
-			r.volumeKeys = append(r.volumeKeys, pathValue)
+			r.VolumeKeys = append(r.VolumeKeys, pathValue)
 		} else if strings.HasPrefix(xDescriptor, attributePrefix) {
-			r.store(path, []byte(pathValue))
+			r.store(cr, path, []byte(pathValue))
 		} else {
 			log.Debug("Defaulting....")
 		}
@@ -122,14 +122,14 @@ func (r *Retriever) read(place, path string, xDescriptors []string) error {
 
 	for name, items := range secrets {
 		// loading secret items all-at-once
-		err := r.readSecret(name, items, place, path)
+		err := r.readSecret(cr, name, items, place, path)
 		if err != nil {
 			return err
 		}
 	}
 	for name, items := range configMaps {
 		// add the function readConfigMap
-		err := r.readConfigMap(name, items, place, path)
+		err := r.readConfigMap(cr, name, items, place, path)
 		if err != nil {
 			return err
 		}
@@ -167,21 +167,17 @@ 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(
-	name string,
-	items []string,
-	fromPath string,
-	path string) error {
+func (r *Retriever) readSecret(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...")
 
 	gvr := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}
-	u, err := r.client.Resource(gvr).Namespace(r.plan.Ns).Get(name, metav1.GetOptions{})
+	secret, err := r.client.Resource(gvr).Namespace(r.plan.Ns).Get(name, metav1.GetOptions{})
 	if err != nil {
 		return err
 	}
 
-	data, exists, err := unstructured.NestedMap(u.Object, []string{"data"}...)
+	data, exists, err := unstructured.NestedMap(secret.Object, []string{"data"}...)
 	if err != nil {
 		return err
 	}
@@ -201,21 +197,17 @@ func (r *Retriever) readSecret(
 		// 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(fmt.Sprintf("configMap_%s", k), data)
-		r.store(fmt.Sprintf("secret_%s", k), data)
+		r.store(cr, fmt.Sprintf("configMap_%s", k), data)
+		r.store(cr, fmt.Sprintf("secret_%s", k), data)
 	}
 
-	r.Objects = append(r.Objects, u)
+	r.Objects = append(r.Objects, secret)
 	return nil
 }
 
 // readConfigMap based in configMap name and list of items, read a configMap from the same namespace informed
 // in plan instance.
-func (r *Retriever) readConfigMap(
-	name string,
-	items []string,
-	fromPath string,
-	path string) error {
+func (r *Retriever) readConfigMap(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...")
 
@@ -244,7 +236,7 @@ func (r *Retriever) readConfigMap(
 		// 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(fmt.Sprintf("configMap_%s", k), []byte(value))
+		r.store(cr, fmt.Sprintf("configMap_%s", k), []byte(value))
 	}
 
 	r.Objects = append(r.Objects, u)
@@ -252,13 +244,13 @@ func (r *Retriever) readConfigMap(
 }
 
 // store key and value, formatting key to look like an environment variable.
-func (r *Retriever) store(key string, value []byte) {
+func (r *Retriever) store(u *unstructured.Unstructured, key string, value []byte) {
 	key = strings.ReplaceAll(key, ":", "_")
 	key = strings.ReplaceAll(key, ".", "_")
 	if r.bindingPrefix == "" {
-		key = fmt.Sprintf("%s_%s", r.plan.CR.GetKind(), key)
+		key = fmt.Sprintf("%s_%s", u.GetKind(), key)
 	} else {
-		key = fmt.Sprintf("%s_%s_%s", r.bindingPrefix, r.plan.CR.GetKind(), key)
+		key = fmt.Sprintf("%s_%s_%s", r.bindingPrefix, u.GetKind(), key)
 	}
 	key = strings.ToUpper(key)
 	r.data[key] = value
@@ -267,40 +259,32 @@ func (r *Retriever) store(key string, value []byte) {
 // Retrieve loop and read data pointed by the references in plan instance. Also runs through
 // "bindable resources", gathering extra data. It can return error on retrieving and reading
 // resources.
-func (r *Retriever) Retrieve() (map[string][]byte, map[string]interface{}, error) {
-	var err error
-	r.logger.Info("Looking for spec-descriptors in 'spec'...")
-	for _, specDescriptor := range r.plan.CRDDescription.SpecDescriptors {
-		if err = r.read("spec", specDescriptor.Path, specDescriptor.XDescriptors); err != nil {
-			return nil, nil, err
+func (r *Retriever) Retrieve() (map[string][]byte, error) {
+	// read bindable data from the specified resources
+	if r.plan.SBR.Spec.DetectBindingResources {
+		err := r.ReadBindableResourcesData(&r.plan.SBR, r.plan.GetCRs())
+		if err != nil {
+			return nil, err
 		}
 	}
 
-	r.logger.Info("Looking for status-descriptors in 'status'...")
-	for _, statusDescriptor := range r.plan.CRDDescription.StatusDescriptors {
-		if err = r.read("status", statusDescriptor.Path, statusDescriptor.XDescriptors); err != nil {
-			return nil, nil, err
-		}
-	}
+	// read bindable data from the CRDDescription found by the planner
+	for _, relatedResource := range r.plan.GetRelatedResources() {
 
-	if r.plan.SBR.Spec.DetectBindingResources {
-		r.logger.Info("Detecting extra resources for binding...")
-		b := NewDetectBindableResources(&r.plan.SBR, r.plan.CR, []schema.GroupVersionResource{
-			{Group: "", Version: "v1", Resource: "configmaps"},
-			{Group: "", Version: "v1", Resource: "services"},
-			{Group: "route.openshift.io", Version: "v1", Resource: "routes"},
-		}, r.client)
-
-		vals, err := b.GetBindableVariables()
+		err := r.ReadCRDDescriptionData(relatedResource.CR, relatedResource.CRDDescription)
 		if err != nil {
-			return nil, nil, err
-		}
-		for k, v := range vals {
-			r.store(k, []byte(fmt.Sprintf("%v", v)))
+			return nil, err
 		}
 	}
 
-	return r.data, r.cache, nil
+	// gather retriever's read data
+	var err error
+	retrievedData, err := r.Get()
+	if err != nil {
+		return nil, err
+	}
+
+	return retrievedData, nil
 }
 
 // NewRetriever instantiate a new retriever instance.
@@ -311,7 +295,7 @@ func NewRetriever(client dynamic.Interface, plan *Plan, bindingPrefix string) *R
 		Objects:       []*unstructured.Unstructured{},
 		client:        client,
 		plan:          plan,
-		volumeKeys:    []string{},
+		VolumeKeys:    []string{},
 		bindingPrefix: bindingPrefix,
 		cache:         make(map[string]interface{}),
 	}
diff --git a/pkg/controller/servicebindingrequest/sbrcontroller.go b/pkg/controller/servicebindingrequest/sbrcontroller.go
index 5a6f2120..3b4efd66 100644
--- a/pkg/controller/servicebindingrequest/sbrcontroller.go
+++ b/pkg/controller/servicebindingrequest/sbrcontroller.go
@@ -100,7 +100,7 @@ func updateFunc(logger *log.Log) func(updateEvent event.UpdateEvent) bool {
 		if isSecret || isConfigMap {
 			dataFieldsAreEqual, err := compareObjectFields(e.ObjectNew, e.ObjectOld, "data")
 			if err != nil {
-				logger.Error(err, "")
+				logger.Error(err, "error comparing object fields: %s", err.Error())
 				return false
 			}
 
diff --git a/pkg/controller/servicebindingrequest/secret.go b/pkg/controller/servicebindingrequest/secret.go
index 3dffbf76..3b08af83 100644
--- a/pkg/controller/servicebindingrequest/secret.go
+++ b/pkg/controller/servicebindingrequest/secret.go
@@ -20,8 +20,12 @@ type Secret struct {
 
 // customEnvParser parse informed data in order to interpolate with values provided by custom
 // environment component.
-func (s *Secret) customEnvParser(data map[string][]byte, cache map[string]interface{}) (map[string][]byte, error) {
+func (s *Secret) customEnvParser(data map[string][]byte) (map[string][]byte, error) {
 	// transforming input into format expected by custom environment parser
+	cache := make(map[string]interface{})
+	for k, v := range data {
+		cache[k] = v
+	}
 
 	// interpolating custom environment
 	envParser := NewCustomEnvParser(s.plan.SBR.Spec.CustomEnvVar, cache)
@@ -80,8 +84,8 @@ func (s *Secret) createOrUpdate(payload map[string][]byte) (*unstructured.Unstru
 
 // Commit will store informed data as a secret, commit it against the API server. It can forward
 // errors from custom environment parser component, or from the API server itself.
-func (s *Secret) Commit(data map[string][]byte, cache map[string]interface{}) (*unstructured.Unstructured, error) {
-	payload, err := s.customEnvParser(data, cache)
+func (s *Secret) Commit(data map[string][]byte) (*unstructured.Unstructured, error) {
+	payload, err := s.customEnvParser(data)
 	if err != nil {
 		return nil, err
 	}
diff --git a/test/e2e/empty.yaml b/test/e2e/empty.yaml
new file mode 100644
index 00000000..e69de29b
diff --git a/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/entrypoint.go b/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/entrypoint.go
index c5ad6eab..24080b72 100644
--- a/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/entrypoint.go
+++ b/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/entrypoint.go
@@ -38,14 +38,5 @@ func (e *Entrypoint) GetInput() (input.Input, error) {
 
 const entrypointTmpl = `#!/bin/sh -e
 
-# This is documented here:
-# https://docs.openshift.com/container-platform/3.11/creating_images/guidelines.html#openshift-specific-guidelines
-
-if ! whoami &>/dev/null; then
-  if [ -w /etc/passwd ]; then
-    echo "${USER_NAME:-{{.ProjectName}}}:x:$(id -u):$(id -g):${USER_NAME:-{{.ProjectName}}} user:${HOME}:/sbin/nologin" >> /etc/passwd
-  fi
-fi
-
 exec ${OPERATOR} $@
 `
diff --git a/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/go_mod.go b/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/go_mod.go
index 841fe1df..ef55786f 100644
--- a/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/go_mod.go
+++ b/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/go_mod.go
@@ -40,7 +40,7 @@ const goModTmpl = `module {{ .Repo }}
 go 1.13
 
 require (
-	github.com/operator-framework/operator-sdk v0.15.1
+	github.com/operator-framework/operator-sdk v0.15.2
 	sigs.k8s.io/controller-runtime v0.4.0
 )
 
diff --git a/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/usersetup.go b/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/usersetup.go
index 200015e2..662be31a 100644
--- a/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/usersetup.go
+++ b/vendor/github.com/operator-framework/operator-sdk/internal/scaffold/usersetup.go
@@ -40,13 +40,11 @@ const userSetupTmpl = `#!/bin/sh
 set -x
 
 # ensure $HOME exists and is accessible by group 0 (we don't know what the runtime UID will be)
+echo "${USER_NAME}:x:${USER_UID}:0:${USER_NAME} user:${HOME}:/sbin/nologin" >> /etc/passwd
 mkdir -p ${HOME}
 chown ${USER_UID}:0 ${HOME}
 chmod ug+rwx ${HOME}
 
-# runtime user will need to be able to self-insert in /etc/passwd
-chmod g+rw /etc/passwd
-
 # no need for this script to remain in the image after running
 rm $0
 `
diff --git a/vendor/github.com/operator-framework/operator-sdk/pkg/restmapper/dynamicrestmapper.go b/vendor/github.com/operator-framework/operator-sdk/pkg/restmapper/dynamicrestmapper.go
deleted file mode 100644
index 0edbe3ea..00000000
--- a/vendor/github.com/operator-framework/operator-sdk/pkg/restmapper/dynamicrestmapper.go
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2019 The Operator-SDK Authors
-//
-// 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 restmapper
-
-import (
-	"k8s.io/apimachinery/pkg/api/meta"
-	"k8s.io/apimachinery/pkg/runtime/schema"
-	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-	"k8s.io/client-go/discovery"
-	"k8s.io/client-go/rest"
-	"k8s.io/client-go/restmapper"
-)
-
-// Deprecated: DynamicRESTMapper exists for historical compatibility
-// and should not be used. See that it was implemented in the controller-runtime.
-// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/554
-type DynamicRESTMapper struct {
-	client   discovery.DiscoveryInterface
-	delegate meta.RESTMapper
-}
-
-// NewDynamicRESTMapper returns a RESTMapper that dynamically discovers resource
-// types at runtime. This is in contrast to controller-manager's default RESTMapper, which
-// only checks resource types at startup, and so can't handle the case of first creating a
-// CRD and then creating an instance of that CRD.
-//
-// Deprecated: NewDynamicRESTMapper exists for historical compatibility
-// and should not be used. See that it was implemented in the controller-runtime.
-// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/554
-func NewDynamicRESTMapper(cfg *rest.Config) (meta.RESTMapper, error) {
-	client, err := discovery.NewDiscoveryClientForConfig(cfg)
-	if err != nil {
-		return nil, err
-	}
-
-	drm := &DynamicRESTMapper{client: client}
-	if err := drm.reload(); err != nil {
-		return nil, err
-	}
-	return drm, nil
-}
-
-func (drm *DynamicRESTMapper) reload() error {
-	gr, err := restmapper.GetAPIGroupResources(drm.client)
-	if err != nil {
-		return err
-	}
-	drm.delegate = restmapper.NewDiscoveryRESTMapper(gr)
-	return nil
-}
-
-// reloadOnError checks if an error indicates that the delegated RESTMapper needs to be
-// reloaded, and if so, reloads it and returns true.
-func (drm *DynamicRESTMapper) reloadOnError(err error) bool {
-	if _, matches := err.(*meta.NoKindMatchError); !matches {
-		return false
-	}
-	err = drm.reload()
-	if err != nil {
-		utilruntime.HandleError(err)
-	}
-	return err == nil
-}
-
-// Deprecated: KindFor exists for historical compatibility
-// and should not be used. See that it was implemented in the controller-runtime.
-// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/554
-func (drm *DynamicRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
-	gvk, err := drm.delegate.KindFor(resource)
-	if drm.reloadOnError(err) {
-		gvk, err = drm.delegate.KindFor(resource)
-	}
-	return gvk, err
-}
-
-// Deprecated: KindsFor exists for historical compatibility
-// and should not be used. See that it was implemented in the controller-runtime.
-// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/554
-func (drm *DynamicRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
-	gvks, err := drm.delegate.KindsFor(resource)
-	if drm.reloadOnError(err) {
-		gvks, err = drm.delegate.KindsFor(resource)
-	}
-	return gvks, err
-}
-
-// Deprecated: ResourceFor exists for historical compatibility
-// and should not be used. See that it was implemented in the controller-runtime.
-// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/554
-func (drm *DynamicRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
-	gvr, err := drm.delegate.ResourceFor(input)
-	if drm.reloadOnError(err) {
-		gvr, err = drm.delegate.ResourceFor(input)
-	}
-	return gvr, err
-}
-
-// Deprecated: ResourcesFor exists for historical compatibility
-// and should not be used. See that it was implemented in the controller-runtime.
-// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/554
-func (drm *DynamicRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
-	gvrs, err := drm.delegate.ResourcesFor(input)
-	if drm.reloadOnError(err) {
-		gvrs, err = drm.delegate.ResourcesFor(input)
-	}
-	return gvrs, err
-}
-
-// Deprecated: RESTMapping exists for historical compatibility
-// and should not be used. See that it was implemented in the controller-runtime.
-// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/554
-func (drm *DynamicRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
-	m, err := drm.delegate.RESTMapping(gk, versions...)
-	if drm.reloadOnError(err) {
-		m, err = drm.delegate.RESTMapping(gk, versions...)
-	}
-	return m, err
-}
-
-// Deprecated: RESTMappings exists for historical compatibility
-// and should not be used. See that it was implemented in the controller-runtime.
-// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/554
-func (drm *DynamicRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
-	ms, err := drm.delegate.RESTMappings(gk, versions...)
-	if drm.reloadOnError(err) {
-		ms, err = drm.delegate.RESTMappings(gk, versions...)
-	}
-	return ms, err
-}
-
-// Deprecated: ResourceSingularizer exists for historical compatibility
-// and should not be used. See that it was implemented in the controller-runtime.
-// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/554
-func (drm *DynamicRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
-	s, err := drm.delegate.ResourceSingularizer(resource)
-	if drm.reloadOnError(err) {
-		s, err = drm.delegate.ResourceSingularizer(resource)
-	}
-	return s, err
-}
diff --git a/vendor/github.com/operator-framework/operator-sdk/version/version.go b/vendor/github.com/operator-framework/operator-sdk/version/version.go
index 4b79e821..de30c9af 100644
--- a/vendor/github.com/operator-framework/operator-sdk/version/version.go
+++ b/vendor/github.com/operator-framework/operator-sdk/version/version.go
@@ -20,7 +20,7 @@ import (
 )
 
 var (
-	Version    = "v0.15.1"
+	Version    = "v0.15.2"
 	GitVersion = "unknown"
 	GitCommit  = "unknown"
 	GoVersion  = fmt.Sprintf("%s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH)
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 51d30669..83ce6f34 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -139,7 +139,7 @@ github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient
 github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister
 github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil
 github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version
-# github.com/operator-framework/operator-sdk v0.15.1
+# github.com/operator-framework/operator-sdk v0.15.2
 github.com/operator-framework/operator-sdk/internal/scaffold
 github.com/operator-framework/operator-sdk/internal/scaffold/input
 github.com/operator-framework/operator-sdk/internal/scaffold/internal/deps
@@ -152,7 +152,6 @@ github.com/operator-framework/operator-sdk/pkg/kube-metrics
 github.com/operator-framework/operator-sdk/pkg/leader
 github.com/operator-framework/operator-sdk/pkg/log/zap
 github.com/operator-framework/operator-sdk/pkg/metrics
-github.com/operator-framework/operator-sdk/pkg/restmapper
 github.com/operator-framework/operator-sdk/pkg/test
 github.com/operator-framework/operator-sdk/pkg/test/e2eutil
 github.com/operator-framework/operator-sdk/version


Test patch:
diff --git a/pkg/controller/servicebindingrequest/binder_test.go b/pkg/controller/servicebindingrequest/binder_test.go
index e5f517b4..7469c995 100644
--- a/pkg/controller/servicebindingrequest/binder_test.go
+++ b/pkg/controller/servicebindingrequest/binder_test.go
@@ -300,19 +300,11 @@ func TestKnativeServicesContractWithBinder(t *testing.T) {
 
 	require.NotNil(t, binder)
 
-	updatedObjects, err := binder.Bind()
-	require.NoError(t, err)
-	require.Len(t, updatedObjects, 1)
-
-	containersPath := []string{"spec", "template", "spec", "containers"}
-	containers, found, err := unstructured.NestedSlice(updatedObjects[0].Object, containersPath...)
-	require.NoError(t, err)
-	require.True(t, found)
-	require.Len(t, containers, 1)
-
-	c := corev1.Container{}
-	u := containers[0].(map[string]interface{})
-	err = runtime.DefaultUnstructuredConverter.FromUnstructured(u, &c)
-	require.NoError(t, err)
-	require.Equal(t, name, c.EnvFrom[0].SecretRef.Name)
+	t.Run("Knative service contract with service binding operator", func(t *testing.T) {
+		list, err := binder.search()
+		assert.Nil(t, err)
+		assert.Equal(t, 1, len(list.Items))
+
+	})
+
 }
diff --git a/pkg/controller/servicebindingrequest/binding_test.go b/pkg/controller/servicebindingrequest/binding_test.go
index 7a7a0c26..1f936e32 100644
--- a/pkg/controller/servicebindingrequest/binding_test.go
+++ b/pkg/controller/servicebindingrequest/binding_test.go
@@ -18,8 +18,6 @@ import (
 
 // TestServiceBinder_Bind exercises scenarios regarding binding SBR and its related resources.
 func TestServiceBinder_Bind(t *testing.T) {
-	t.Skip("functionality is not ready yet")
-
 	// wantedAction represents an action issued by the component that is required to exist after it
 	// finished the operation
 	type wantedAction struct {
@@ -128,20 +126,24 @@ func TestServiceBinder_Bind(t *testing.T) {
 	db1 := f.AddMockedUnstructuredPostgresDatabaseCR("db1")
 	{
 		runtimeStatus := map[string]interface{}{
-			"dbConfigMap": "db1",
+			"dbConfigMap":   "db1",
+			"dbCredentials": "db1",
 		}
 		err := unstructured.SetNestedMap(db1.Object, runtimeStatus, "status")
 		require.NoError(t, err)
 	}
+	f.AddMockedSecret("db1")
 
 	db2 := f.AddMockedUnstructuredPostgresDatabaseCR("db2")
 	{
 		runtimeStatus := map[string]interface{}{
-			"dbConfigMap": "db2",
+			"dbConfigMap":   "db2",
+			"dbCredentials": "db2",
 		}
 		err := unstructured.SetNestedMap(db2.Object, runtimeStatus, "status")
 		require.NoError(t, err)
 	}
+	f.AddMockedSecret("db2")
 
 	// create the ServiceBindingRequest
 	sbrSingleService := &v1alpha1.ServiceBindingRequest{
diff --git a/pkg/controller/servicebindingrequest/bindinginfo_test.go b/pkg/controller/servicebindingrequest/bindinginfo_test.go
index 331d3871..5b7d30ae 100644
--- a/pkg/controller/servicebindingrequest/bindinginfo_test.go
+++ b/pkg/controller/servicebindingrequest/bindinginfo_test.go
@@ -1,6 +1,9 @@
 package servicebindingrequest
 
-import "testing"
+import (
+	"github.com/stretchr/testify/require"
+	"testing"
+)
 
 // TestNewBindingInfo exercises annotation binding information parsing.
 func TestNewBindingInfo(t *testing.T) {
@@ -42,7 +45,7 @@ func TestNewBindingInfo(t *testing.T) {
 				t.Errorf("NewBindingInfo() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			} else if err == nil {
-				requireYamlEqual(t, tt.want, b)
+				require.Equal(t, tt.want, b)
 			}
 		})
 	}
diff --git a/pkg/controller/servicebindingrequest/olm_test.go b/pkg/controller/servicebindingrequest/olm_test.go
index 58232c8b..8997454d 100644
--- a/pkg/controller/servicebindingrequest/olm_test.go
+++ b/pkg/controller/servicebindingrequest/olm_test.go
@@ -5,24 +5,14 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/pmezard/go-difflib/difflib"
 	"github.com/stretchr/testify/require"
-	"gopkg.in/yaml.v2"
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/types"
 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
 
-	olmv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
-
 	"github.com/redhat-developer/service-binding-operator/test/mocks"
 )
 
-const (
-	dataUserAnnotation     = "servicebindingoperator.redhat.io/status.dbCredentials-data.user"
-	dataPasswordAnnotation = "servicebindingoperator.redhat.io/status.dbCredentials-data.password"
-)
-
 func init() {
 	logf.SetLogger(logf.ZapLogger(true))
 }
@@ -57,9 +47,9 @@ func TestOLMNew(t *testing.T) {
 		require.Len(t, crds, 1)
 	})
 
-	t.Run("SelectCRDDescriptionByGVK", func(t *testing.T) {
+	t.Run("SelectCRDByGVK", func(t *testing.T) {
 		// FIXME: include test for populated CRD
-		crd, err := olm.SelectCRDDescriptionByGVK(schema.GroupVersionKind{
+		crd, err := olm.SelectCRDByGVK(schema.GroupVersionKind{
 			Group:   mocks.CRDName,
 			Version: mocks.CRDVersion,
 			Kind:    mocks.CRDKind,
@@ -85,131 +75,3 @@ func TestOLMNew(t *testing.T) {
 		assertGVKs(t, gvks)
 	})
 }
-
-func TestOLMBuildDescriptorsFromAnnotations(t *testing.T) {
-	type args struct {
-		annotations map[string]string
-	}
-	tests := []struct {
-		name             string
-		args             args
-		specDescriptor   *olmv1alpha1.SpecDescriptor
-		statusDescriptor *olmv1alpha1.StatusDescriptor
-		wantErr          bool
-	}{
-		{
-			name: "secret",
-			args: args{
-				annotations: map[string]string{
-					dataUserAnnotation:     "binding:env:object:secret",
-					dataPasswordAnnotation: "binding:env:object:secret",
-				},
-			},
-			specDescriptor: nil,
-			statusDescriptor: &olmv1alpha1.StatusDescriptor{
-				Path:        "dbCredentials",
-				DisplayName: "",
-				Description: "",
-				XDescriptors: []string{
-					"binding:env:object:secret:data.password",
-					"binding:env:object:secret:data.user",
-				},
-				Value: nil,
-			},
-			wantErr: false,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			specDescriptor, statusDescriptor, err :=
-				buildDescriptorsFromAnnotations(tt.args.annotations)
-			if (err != nil) != tt.wantErr {
-				t.Errorf(
-					"buildDescriptorsFromAnnotations() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-
-			requireYamlEqual(t, specDescriptor, tt.specDescriptor)
-			requireYamlEqual(t, statusDescriptor, tt.statusDescriptor)
-		})
-	}
-}
-
-func TestOLMBuildCRDDescriptionFromCRDAnnotations(t *testing.T) {
-	type args struct {
-		crd *unstructured.Unstructured
-	}
-
-	tests := []struct {
-		name           string
-		args           args
-		crdDescription *olmv1alpha1.CRDDescription
-		wantErr        bool
-	}{
-		{
-			name: "happy path",
-			args: args{
-				crd: &unstructured.Unstructured{
-					Object: map[string]interface{}{
-						"spec": map[string]interface{}{
-							"names": map[string]interface{}{
-								"kind": "CustomResourceDefinitionKind",
-							},
-							"version": "v1alpha1",
-						},
-						"metadata": map[string]interface{}{
-							"annotations": map[string]interface{}{
-								dataUserAnnotation:     "binding:env:object:secret",
-								dataPasswordAnnotation: "binding:env:object:secret",
-							},
-						},
-					},
-				},
-			},
-			crdDescription: &olmv1alpha1.CRDDescription{
-				Kind:    "CustomResourceDefinitionKind",
-				Version: "v1alpha1",
-				StatusDescriptors: []olmv1alpha1.StatusDescriptor{
-					{
-						Path: "dbCredentials",
-						XDescriptors: []string{
-							"binding:env:object:secret:data.password",
-							"binding:env:object:secret:data.user",
-						},
-					},
-				},
-			},
-			wantErr: false,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			crdDescription, err := buildCRDDescriptionFromCRD(tt.args.crd)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("buildCRDDescriptionFromCRD() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-			requireYamlEqual(t, crdDescription, tt.crdDescription)
-		})
-	}
-}
-
-// requireYamlEqual compares two values and return a message with the different context
-func requireYamlEqual(t *testing.T, a interface{}, b interface{}) {
-	yamlActual, _ := yaml.Marshal(a)
-	yamlExpected, _ := yaml.Marshal(b)
-
-	diff := difflib.UnifiedDiff{
-		A:        difflib.SplitLines(string(yamlExpected)),
-		B:        difflib.SplitLines(string(yamlActual)),
-		FromFile: "Expected",
-		ToFile:   "Actual",
-		Context:  4,
-	}
-
-	message, err := difflib.GetUnifiedDiffString(diff)
-	require.NoError(t, err)
-	if len(message) > 0 {
-		t.Errorf("expected is different from actual:\n%s", message)
-	}
-}
diff --git a/pkg/controller/servicebindingrequest/planner_test.go b/pkg/controller/servicebindingrequest/planner_test.go
index 2d0fc38d..cea2693d 100644
--- a/pkg/controller/servicebindingrequest/planner_test.go
+++ b/pkg/controller/servicebindingrequest/planner_test.go
@@ -4,9 +4,11 @@ import (
 	"context"
 	"testing"
 
-	"github.com/redhat-developer/service-binding-operator/test/mocks"
 	"github.com/stretchr/testify/require"
 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
+
+	"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1"
+	"github.com/redhat-developer/service-binding-operator/test/mocks"
 )
 
 var planner *Planner
@@ -25,6 +27,9 @@ func TestPlannerNew(t *testing.T) {
 	}
 	f := mocks.NewFake(t, ns)
 	sbr := f.AddMockedServiceBindingRequest(name, resourceRef, "", deploymentsGVR, matchLabels)
+	sbr.Spec.BackingServiceSelectors = []v1alpha1.BackingServiceSelector{
+		sbr.Spec.BackingServiceSelector,
+	}
 	f.AddMockedUnstructuredCSV("cluster-service-version")
 	f.AddMockedDatabaseCR(resourceRef)
 	f.AddMockedUnstructuredDatabaseCRD()
@@ -33,7 +38,7 @@ func TestPlannerNew(t *testing.T) {
 	require.NotNil(t, planner)
 
 	t.Run("searchCR", func(t *testing.T) {
-		cr, err := planner.searchCR()
+		cr, err := planner.searchCR(ns, sbr.Spec.BackingServiceSelector)
 
 		require.NoError(t, err)
 		require.NotNil(t, cr)
@@ -44,8 +49,7 @@ func TestPlannerNew(t *testing.T) {
 
 		require.NoError(t, err)
 		require.NotNil(t, plan)
-		require.NotNil(t, plan.CRDDescription)
-		require.NotNil(t, plan.CR)
+		require.NotEmpty(t, plan.RelatedResources)
 		require.Equal(t, ns, plan.Ns)
 		require.Equal(t, name, plan.Name)
 	})
@@ -62,12 +66,13 @@ func TestPlannerAnnotation(t *testing.T) {
 	f := mocks.NewFake(t, ns)
 	sbr := f.AddMockedServiceBindingRequest(name, resourceRef, "", deploymentsGVR, matchLabels)
 	f.AddMockedUnstructuredDatabaseCRD()
+	cr := f.AddMockedDatabaseCR("database")
 
 	planner = NewPlanner(context.TODO(), f.FakeDynClient(), sbr)
 	require.NotNil(t, planner)
 
 	t.Run("searchCRD", func(t *testing.T) {
-		crd, err := planner.searchCRD()
+		crd, err := planner.searchCRD(cr.GetObjectKind().GroupVersionKind())
 
 		require.NoError(t, err)
 		require.NotNil(t, crd)
diff --git a/pkg/controller/servicebindingrequest/reconciler_test.go b/pkg/controller/servicebindingrequest/reconciler_test.go
index dc52b138..e6c81163 100644
--- a/pkg/controller/servicebindingrequest/reconciler_test.go
+++ b/pkg/controller/servicebindingrequest/reconciler_test.go
@@ -46,7 +46,9 @@ func TestReconcilerReconcileError(t *testing.T) {
 		"environment": "reconciler",
 	}
 	f := mocks.NewFake(t, reconcilerNs)
+	f.AddMockedUnstructuredDatabaseCRD()
 	f.AddMockedUnstructuredServiceBindingRequest(reconcilerName, backingServiceResourceRef, "", deploymentsGVR, matchLabels)
+	f.AddMockedUnstructuredPostgresDatabaseCR("test-using-secret")
 
 	fakeClient := f.FakeClient()
 	fakeDynClient := f.FakeDynClient()
@@ -54,15 +56,10 @@ func TestReconcilerReconcileError(t *testing.T) {
 
 	res, err := reconciler.Reconcile(reconcileRequest())
 
-	// FIXME: decide this test's fate
-	// I'm not very sure what this test was about, but in the case the SBR definition contains
-	// references to objects that do not exist, the reconciliation process is supposed to be
-	// successful. Commented below was the original test.
-	//
-	// require.Error(t, err)
-	// require.True(t, res.Requeue)
-
-	require.NoError(t, err)
+	// currently this test passes because annotations present in the Databases CRD being currently
+	// used doesn't have a 'status' field in its definition; once it does and this code is updated (
+	// since the Postgres CRD is being imported to be used in tests) this test will fail.
+	require.Error(t, err)
 	require.True(t, res.Requeue)
 }
 
diff --git a/pkg/controller/servicebindingrequest/retriever_test.go b/pkg/controller/servicebindingrequest/retriever_test.go
index 7c8d73de..a2486046 100644
--- a/pkg/controller/servicebindingrequest/retriever_test.go
+++ b/pkg/controller/servicebindingrequest/retriever_test.go
@@ -25,29 +25,39 @@ func TestRetriever(t *testing.T) {
 	cr, err := mocks.UnstructuredDatabaseCRMock(ns, crName)
 	require.NoError(t, err)
 
-	plan := &Plan{Ns: ns, Name: "retriever", CRDDescription: &crdDescription, CR: cr}
+	plan := &Plan{
+		Ns:   ns,
+		Name: "retriever",
+		RelatedResources: []*RelatedResource{
+			{
+				CRDDescription: &crdDescription,
+				CR:             cr,
+			},
+		},
+	}
 
 	fakeDynClient := f.FakeDynClient()
 
 	retriever = NewRetriever(fakeDynClient, plan, "SERVICE_BINDING")
 	require.NotNil(t, retriever)
 
-	t.Run("retrive", func(t *testing.T) {
-		objs, _, err := retriever.Retrieve()
+	t.Run("retrieve", func(t *testing.T) {
+		_ = retriever.ReadCRDDescriptionData(cr, &crdDescription)
+		objs, err := retriever.Retrieve()
 		require.NoError(t, err)
 		require.NotEmpty(t, retriever.data)
 		require.True(t, len(objs) > 0)
 	})
 
 	t.Run("getCRKey", func(t *testing.T) {
-		imageName, _, err := retriever.getCRKey("spec", "imageName")
+		imageName, _, err := retriever.getCRKey(cr, "spec", "imageName")
 		require.NoError(t, err)
 		require.Equal(t, "postgres", imageName)
 	})
 
 	t.Run("read", func(t *testing.T) {
 		// reading from secret, from status attribute
-		err := retriever.read("status", "dbCredentials", []string{
+		err := retriever.read(cr, "status", "dbCredentials", []string{
 			"binding:env:object:secret:user",
 			"binding:env:object:secret:password",
 		})
@@ -58,7 +68,7 @@ func TestRetriever(t *testing.T) {
 		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_SECRET_PASSWORD")
 
 		// reading from spec attribute
-		err = retriever.read("spec", "image", []string{
+		err = retriever.read(cr, "spec", "image", []string{
 			"binding:env:attribute",
 		})
 		require.NoError(t, err)
@@ -76,7 +86,7 @@ func TestRetriever(t *testing.T) {
 	t.Run("readSecret", func(t *testing.T) {
 		retriever.data = make(map[string][]byte)
 
-		err := retriever.readSecret("db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
+		err := retriever.readSecret(cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
 		require.NoError(t, err)
 
 		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_SECRET_USER")
@@ -84,7 +94,7 @@ func TestRetriever(t *testing.T) {
 	})
 
 	t.Run("store", func(t *testing.T) {
-		retriever.store("test", []byte("test"))
+		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"])
 	})
@@ -94,7 +104,7 @@ func TestRetriever(t *testing.T) {
 		require.NotNil(t, retriever)
 		retriever.data = make(map[string][]byte)
 
-		err := retriever.readSecret("db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
+		err := retriever.readSecret(cr, "db-credentials", []string{"user", "password"}, "spec", "dbConfigMap")
 		require.NoError(t, err)
 
 		require.Contains(t, retriever.data, "DATABASE_SECRET_USER")
@@ -117,7 +127,16 @@ func TestRetrieverWithNestedCRKey(t *testing.T) {
 	cr, err := mocks.UnstructuredNestedDatabaseCRMock(ns, crName)
 	require.NoError(t, err)
 
-	plan := &Plan{Ns: ns, Name: "retriever", CRDDescription: &crdDescription, CR: cr}
+	plan := &Plan{
+		Ns:   ns,
+		Name: "retriever",
+		RelatedResources: []*RelatedResource{
+			{
+				CRDDescription: &crdDescription,
+				CR:             cr,
+			},
+		},
+	}
 
 	fakeDynClient := f.FakeDynClient()
 
@@ -125,7 +144,7 @@ func TestRetrieverWithNestedCRKey(t *testing.T) {
 	require.NotNil(t, retriever)
 
 	t.Run("Second level", func(t *testing.T) {
-		imageName, _, err := retriever.getCRKey("spec", "image.name")
+		imageName, _, err := retriever.getCRKey(cr, "spec", "image.name")
 		require.NoError(t, err)
 		require.Equal(t, "postgres", imageName)
 	})
@@ -133,12 +152,12 @@ func TestRetrieverWithNestedCRKey(t *testing.T) {
 	t.Run("Second level error", func(t *testing.T) {
 		// FIXME: if attribute isn't available in CR we would not throw any error.
 		t.Skip()
-		_, _, err := retriever.getCRKey("spec", "image..name")
+		_, _, err := retriever.getCRKey(cr, "spec", "image..name")
 		require.NotNil(t, err)
 	})
 
 	t.Run("Third level", func(t *testing.T) {
-		something, _, err := retriever.getCRKey("spec", "image.third.something")
+		something, _, err := retriever.getCRKey(cr, "spec", "image.third.something")
 		require.NoError(t, err)
 		require.Equal(t, "somevalue", something)
 	})
@@ -161,7 +180,16 @@ func TestRetrieverWithConfigMap(t *testing.T) {
 	cr, err := mocks.UnstructuredDatabaseConfigMapMock(ns, crName, crName)
 	require.NoError(t, err)
 
-	plan := &Plan{Ns: ns, Name: "retriever", CRDDescription: &crdDescription, CR: cr}
+	plan := &Plan{
+		Ns:   ns,
+		Name: "retriever",
+		RelatedResources: []*RelatedResource{
+			{
+				CRDDescription: &crdDescription,
+				CR:             cr,
+			},
+		},
+	}
 
 	fakeDynClient := f.FakeDynClient()
 
@@ -170,7 +198,7 @@ func TestRetrieverWithConfigMap(t *testing.T) {
 
 	t.Run("read", func(t *testing.T) {
 		// reading from configMap, from status attribute
-		err = retriever.read("spec", "dbConfigMap", []string{
+		err = retriever.read(cr, "spec", "dbConfigMap", []string{
 			"binding:env:object:configmap:user",
 			"binding:env:object:configmap:password",
 		})
@@ -180,6 +208,7 @@ func TestRetrieverWithConfigMap(t *testing.T) {
 		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_CONFIGMAP_USER")
 		require.Contains(t, retriever.data, "SERVICE_BINDING_DATABASE_CONFIGMAP_PASSWORD")
 	})
+
 	t.Run("extractConfigMapItemName", func(t *testing.T) {
 		require.Equal(t, "user", retriever.extractConfigMapItemName(
 			"binding:env:object:configmap:user"))
@@ -188,7 +217,7 @@ func TestRetrieverWithConfigMap(t *testing.T) {
 	t.Run("readConfigMap", func(t *testing.T) {
 		retriever.data = make(map[string][]byte)
 
-		err := retriever.readConfigMap(crName, []string{"user", "password"}, "spec", "dbConfigMap")
+		err := retriever.readConfigMap(cr, crName, []string{"user", "password"}, "spec", "dbConfigMap")
 		require.NoError(t, err)
 
 		require.Contains(t, retriever.data, ("SERVICE_BINDING_DATABASE_CONFIGMAP_USER"))
@@ -204,13 +233,23 @@ func TestCustomEnvParser(t *testing.T) {
 	crName := "db-testing"
 
 	f := mocks.NewFake(t, ns)
+	f.AddMockedUnstructuredCSV("csv")
 	f.AddMockedSecret("db-credentials")
 
 	crdDescription := mocks.CRDDescriptionMock()
 	cr, err := mocks.UnstructuredDatabaseCRMock(ns, crName)
 	require.NoError(t, err)
 
-	plan := &Plan{Ns: ns, Name: "retriever", CRDDescription: &crdDescription, CR: cr}
+	plan := &Plan{
+		Ns:   ns,
+		Name: "retriever",
+		RelatedResources: []*RelatedResource{
+			{
+				CRDDescription: &crdDescription,
+				CR:             cr,
+			},
+		},
+	}
 
 	fakeDynClient := f.FakeDynClient()
 
@@ -218,9 +257,12 @@ func TestCustomEnvParser(t *testing.T) {
 	require.NotNil(t, retriever)
 
 	t.Run("Should detect custom env values", func(t *testing.T) {
-		_, _, err = retriever.Retrieve()
+		_ = retriever.ReadCRDDescriptionData(cr, &crdDescription)
+		_, err = retriever.Retrieve()
 		require.NoError(t, err)
 
+		t.Logf("\nCache %+v", retriever.cache)
+
 		envMap := []corev1.EnvVar{
 			{
 				Name:  "JDBC_CONNECTION_STRING",
diff --git a/pkg/controller/servicebindingrequest/secret_test.go b/pkg/controller/servicebindingrequest/secret_test.go
index cb30534d..017a12a3 100644
--- a/pkg/controller/servicebindingrequest/secret_test.go
+++ b/pkg/controller/servicebindingrequest/secret_test.go
@@ -21,17 +21,19 @@ func TestSecretNew(t *testing.T) {
 	f := mocks.NewFake(t, ns)
 
 	matchLabels := map[string]string{}
-
 	sbr := mocks.ServiceBindingRequestMock(ns, name, "", "", deploymentsGVR, matchLabels)
 
-	plan := &Plan{Ns: ns, Name: name, CRDDescription: nil, SBR: *sbr}
+	plan := &Plan{
+		Ns:   ns,
+		Name: name,
+		SBR:  *sbr,
+	}
 	data := map[string][]byte{"key": []byte("value")}
 
 	s := NewSecret(f.FakeDynClient(), plan)
-	fakeDynClient := f.FakeDynClient()
-	retriever := NewRetriever(fakeDynClient, plan, "SERVICE_BINDING")
+
 	t.Run("customEnvParser", func(t *testing.T) {
-		customData, err := s.customEnvParser(data, retriever.cache)
+		customData, err := s.customEnvParser(data)
 		assert.NoError(t, err)
 		assert.NotNil(t, customData)
 	})
@@ -48,14 +50,15 @@ func TestSecretNew(t *testing.T) {
 	})
 
 	t.Run("Commit", func(t *testing.T) {
-		u, err := s.Commit(data, retriever.cache)
+		u, err := s.Commit(data)
 		assert.NoError(t, err)
 		assertSecretNamespacedName(t, u, ns, name)
 	})
 
 	t.Run("Get", func(t *testing.T) {
-		u, _, err := s.Get()
+		u, found, err := s.Get()
 		assert.NoError(t, err)
+		assert.True(t, found)
 		assertSecretNamespacedName(t, u, ns, name)
 	})
 }