Repository: redhat-developer/service-binding-operator
Pull request #322 (2020-02-07 15:34:16)

Support multiple backing services
### Motivation

This change enables the user to specify multiple backing services in a ServiceBindRequest.

### Changes

`ServiceBinder` is now used to orchestrate the reconciliation process, and it is multiple services aware. It was introduced in an earlier PR together with internal changes required for multiple services support.

### Testing

Tests haven't been changed, only had their coverage increased as this is a major refactoring.

See `examples/multiple-services/example/README.md` for the demo use case, which offers a step-by-step guide to observe the functionality.

--------
```
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
```

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 **isutton**
You can assign the PR to them by writing `/assign @isutton` 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":["isutton"]} -->
openshift-ci-robot: @isutton: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/lint | 6d574e436ceac33102d7c40b7da54575d446350a | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-lint/1047) | `/test lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=322). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:isutton). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @isutton: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/lint | 6d574e436ceac33102d7c40b7da54575d446350a | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-lint/1047) | `/test lint`
ci/prow/unit | 6d574e436ceac33102d7c40b7da54575d446350a | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-unit/1014) | `/test unit`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=322). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:isutton). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @isutton: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/lint | 6d574e436ceac33102d7c40b7da54575d446350a | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-lint/1047) | `/test lint`
ci/prow/unit | 6d574e436ceac33102d7c40b7da54575d446350a | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-unit/1014) | `/test unit`
ci/prow/e2e | 6d574e436ceac33102d7c40b7da54575d446350a | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-e2e/1101) | `/test e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=322). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:isutton). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @isutton: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/unit | 6d574e436ceac33102d7c40b7da54575d446350a | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-unit/1014) | `/test unit`
ci/prow/e2e | 6d574e436ceac33102d7c40b7da54575d446350a | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-e2e/1101) | `/test e2e`
ci/prow/lint | f0bc0ff67470a61cbf0a6e3f6128c802e7c045a6 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-lint/1049) | `/test lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=322). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:isutton). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
codecov[bot]: # [Codecov](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@06ac5cc`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `78.37%`.

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

```diff
@@            Coverage Diff            @@
##             master     #322   +/-   ##
=========================================
  Coverage          ?   59.03%           
=========================================
  Files             ?       20           
  Lines             ?     1445           
  Branches          ?        0           
=========================================
  Hits              ?      853           
  Misses            ?      434           
  Partials          ?      158
```


| [Impacted Files](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pkg/controller/servicebindingrequest/secret.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3NlY3JldC5nbw==) | `76.66% <100%> (ø)` | |
| [pkg/controller/servicebindingrequest/binding.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L2JpbmRpbmcuZ28=) | `50.38% <100%> (ø)` | |
| [...controller/servicebindingrequest/plan\_decoupled.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3BsYW5fZGVjb3VwbGVkLmdv) | `100% <100%> (ø)` | |
| [pkg/controller/servicebindingrequest/reconciler.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3JlY29uY2lsZXIuZ28=) | `50.61% <100%> (ø)` | |
| [...roller/servicebindingrequest/retrieve\_decoupled.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3JldHJpZXZlX2RlY291cGxlZC5nbw==) | `62.5% <25%> (ø)` | |
| [pkg/controller/servicebindingrequest/olm.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L29sbS5nbw==) | `64.4% <71.42%> (ø)` | |
| [pkg/controller/servicebindingrequest/retriever.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3JldHJpZXZlci5nbw==) | `77.91% <75%> (ø)` | |
| [pkg/controller/servicebindingrequest/planner.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/322/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3BsYW5uZXIuZ28=) | `91.3% <88.88%> (ø)` | |

------

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

openshift-ci-robot: @isutton: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e | 6d574e436ceac33102d7c40b7da54575d446350a | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-e2e/1101) | `/test e2e`
ci/prow/lint | 1095332931a18dde61b93cb57be7f423fb652d6f | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-lint/1050) | `/test lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=322). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:isutton). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
sbose78: @isutton @pmacik could we test this PR and add a sample SBR in the PR description by using a combination of backing services from:

- https://github.com/redhat-developer/service-binding-operator/blob/master/examples/nodejs_etcd_operator/README.md , and 
- https://github.com/redhat-developer/service-binding-operator/blob/master/examples/nodejs_postgresql/README.md
isutton: > @isutton @pmacik could we test this PR and add a sample SBR in the PR description by using a combination of backing services from:
> 
>     * https://github.com/redhat-developer/service-binding-operator/blob/master/examples/nodejs_etcd_operator/README.md , and
> 
>     * https://github.com/redhat-developer/service-binding-operator/blob/master/examples/nodejs_postgresql/README.md

This work is still in progress, one last piece is missing to enable e2e tests which is swap the reconciler logic from the current implementation to use ServiceBinder (which is enabled to bind multiple services).

openshift-ci-robot: @isutton: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/lint | 33bc785e4b516ff9629d2ccec5ce9b896a58e178 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-lint/1067) | `/test lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=322). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:isutton). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
sbose78: Modify internal functions/APIs to take in namespace explicitly. 
openshift-ci-robot: @isutton: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e | 69c1d128d604f75eafe55666be6e601e8d3f639c | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-e2e/1120) | `/test e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=322). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:isutton). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @isutton: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e | 604a143fe505cbf020d285c001dad728061f6d8b | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/322/pull-ci-redhat-developer-service-binding-operator-master-e2e/1127) | `/test e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=322). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:isutton). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
sbose78: @Avni-Sharma @isutton How would a user specify custom env vars in case of multiple backing services?

Could you ensure we have a basic documentation in the PR so that somebody could pick it up and update docs later on?

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/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/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-crds.sh b/hack/check-crds.sh
index b739f1ac..d81562c3 100755
--- a/hack/check-crds.sh
+++ b/hack/check-crds.sh
@@ -19,8 +19,8 @@ CRD_NAME="servicebindingrequests.apps.openshift.io"
 echo "# Searching for '${CRD_NAME}'..."
 
 if ! check_crds ${CRD_NAME} ; then
-    echo "CRD doesn't exist: ${crd_name}"
+    echo "CRD doesn't exist: ${CRD_NAME}"
     exit 1
 fi
 
-echo "CRD is found: ${crd_name}"
+echo "CRD is found: ${CRD_NAME}"
diff --git a/hack/check-csvs.sh b/hack/check-csvs.sh
new file mode 100755
index 00000000..1a2c4656
--- /dev/null
+++ b/hack/check-csvs.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+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
+            return 0
+        fi
+
+        sleep 10
+    done
+
+    return 1
+}
+
+CSV_NAME="service-binding-operator"
+
+echo "# Searching for '${CSV_NAME}'..."
+
+if ! check_csvs ${CSV_NAME} ; then
+    echo "csv doesn't exist: ${CSV_NAME}"
+    exit 1
+fi
+
+echo "csv is found: ${CSV_NAME}"
diff --git a/hack/dev-release.sh b/hack/dev-release.sh
index 1d263f30..bf9b918d 100755
--- a/hack/dev-release.sh
+++ b/hack/dev-release.sh
@@ -15,6 +15,7 @@ if [ ${RUNNING_STATUS} = "Running" ] ; then
 	echo "Operator marketplace pod is running"
 fi
 ./hack/check-crds.sh
+./hack/check-csvs.sh
 INSTALL_PLAN_PRIOR=service-binding-operator.v${BUNDLE_VERSION}
 VERSION_NUMBER=`kubectl get csvs  -n=default -o jsonpath='{.items[*].spec.version}'`
 if [ "${VERSION_NUMBER}" = "${BUNDLE_VERSION}" ] ; then
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/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)
 	})
 }