Repository: redhat-developer/service-binding-operator
Pull request #397 (2020-03-25 16:11:38)

Handle errors for CSV CRD does not installed
### Motivation
Currently the operator will throw error `"olm","msg":"on listting CSVs","error":"the server could not find the requested resource"` if CSV CRD is not installed.
Handle these errors will make service binding operator work with less dependency(With SBR CRD only).

More detials refer to Issue https://github.com/redhat-developer/service-binding-operator/issues/385

### Changes
Handle errors in `olm.listCSVs()` and also check CSV resources before add watching.

### Testing

The fake client will not return 404 error while listing non-existing resources kind. So test case is not present for this error handling. Maybe we could add cases in e2e test.

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 **avni-sharma**
You can assign the PR to them by writing `/assign @avni-sharma` 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":["avni-sharma"]} -->
openshift-ci-robot: Hi @int8dev. Thanks for your PR.

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

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

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

<details>

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

sbose78: /ok-to-test
sbose78: Thank you!

Have you tried out a binding on Kubernetes with no OLM ?
openshift-ci-robot: @int8dev: The following test **failed**, say `/retest` to rerun all failed tests:

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

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=397). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:int8dev). 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 -->
int8dev: Hi @sbose78 
I have tested with using kubernetes 1.15 cluster with SBR CRD installed only. It works like a charm.

int8dev: /retest
codecov[bot]: # [Codecov](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/397?src=pr&el=h1) Report
> Merging [#397](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/397?src=pr&el=desc) into [master](https://codecov.io/gh/redhat-developer/service-binding-operator/commit/a91f3c6ac2ae94d9ec3da1b7ca4cb23f8663b39d?src=pr&el=desc) will **decrease** coverage by `0.5%`.
> The diff coverage is `0%`.

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

```diff
@@            Coverage Diff             @@
##           master     #397      +/-   ##
==========================================
- Coverage   64.74%   64.23%   -0.51%     
==========================================
  Files          21       21              
  Lines        1387     1398      +11     
==========================================
  Hits          898      898              
- Misses        364      374      +10     
- Partials      125      126       +1
```


| [Impacted Files](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/397?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [.../controller/servicebindingrequest/sbrcontroller.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/397/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L3NicmNvbnRyb2xsZXIuZ28=) | `24% <0%> (-1.36%)` | :arrow_down: |
| [pkg/controller/servicebindingrequest/olm.go](https://codecov.io/gh/redhat-developer/service-binding-operator/pull/397/diff?src=pr&el=tree#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZWJpbmRpbmdyZXF1ZXN0L29sbS5nbw==) | `66.85% <0%> (-1.17%)` | :arrow_down: |

------

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

sbose78: @int8dev Thank you very much!
Do you mind sharing the steps of the scenario that you've tested on kube?
int8dev: Here is what I have done for testing.
1. Prepare a kubernetes 1.15 cluster with knative installed
2. Configure necessary role and rolebindings and install SBR CRD only. Build service-binding-operator with this commit and deploy to cluster. 
3. Create a backend service using IBM cloud operator following https://github.com/redhat-developer/service-binding-operator/tree/master/examples/nodejs_ibmcloud_operator. I choosed a speech-to-text service for testing.
4. Create a simple knative service.
5. Create following ServiceBindingRequest accordingly. 
```apiVersion: apps.openshift.io/v1alpha1
kind: ServiceBindingRequest
metadata:
  name: binding-request-speech-to-text
  namespace: default
spec:
  applicationSelector:
    group: serving.knative.dev
    resource: services
    resourceRef: mydemo
    version: v1
  backingServiceSelector:
    group: ibmcloud.ibm.com
    kind: Binding
    resourceRef: my-speech-to-text-binding
    version: v1alpha1
  customEnvVar:
  - name: STX_URL
    value: '{{ index .status.secretName "url" }}'
  - name: STX_APIKEY
    value: '{{ index .status.secretName "apikey" }}'
  detectBindingResources: false
  envVarPrefix: STX
```
6. Check the log of service-binding-operator. Notice there is a warning message complaining that CSV CRD is not installed.
```
{"level":"info","ts":1585231759.7002933,"logger":"reconciler","msg":"Reconciling ServiceBindingRequest...","Request.Namespace":"default","Request.Name":"binding-request-speech-to-text"}
{"level":"info","ts":1585231759.7814934,"logger":"olm","msg":"WARNING: ClusterServiceVersions CRD is not installed"}
{"level":"info","ts":1585231759.8041327,"logger":"retriever","msg":"Looking for spec-descriptors in 'spec'..."}
{"level":"info","ts":1585231759.8041744,"logger":"retriever","msg":"Looking for status-descriptors in 'status'..."}
{"level":"info","ts":1585231759.8191376,"logger":"reconciler","msg":"Starting the bind of application(s) with backing service...","Request.Namespace":"default","Request.Name":"binding-request-speech-to-text","ServiceBindingRequest.Name":"binding-request-speech-to-text"}
{"level":"info","ts":1585231759.819233,"logger":"reconciler.bind","msg":"Binding applications with intermediary secret...","Request.Namespace":"default","Request.Name":"binding-request-speech-to-text","ServiceBindingRequest.Name":"binding-request-speech-to-text"}
{"level":"info","ts":1585231759.8192647,"logger":"reconciler","msg":"Saving data on intermediary secret...","Request.Namespace":"default","Request.Name":"binding-request-speech-to-text","ServiceBindingRequest.Name":"binding-request-speech-to-text"}
{"level":"info","ts":1585231760.0712717,"logger":"reconciler","msg":"All done!","Request.Namespace":"default","Request.Name":"binding-request-speech-to-text","ServiceBindingRequest.Name":"binding-request-speech-to-text"}
```
7. Check the env of ksvc pod
```
root@mydemo-xvh4t-deployment-5b957599b9-sk8zt:/# env|grep STX_
STX_APIKEY=[REDACTED]
STX_BINDING_SECRET_IAM_ROLE_CRN=crn:v1:bluemix:public:iam::::serviceRole:Manager
STX_BINDING_CONFIGMAP_IAM_SERVICEID_CRN=crn:v1:bluemix:public:iam-identity::a/[REDACTED]::serviceid:[REDACTED]
STX_BINDING_CONFIGMAP_IAM_APIKEY_NAME=my-speech-to-text-binding
STX_BINDING_CONFIGMAP_URL=https://api.us-south.speech-to-text.watson.cloud.ibm.com/instances/0f97901f-37a9-4618-b07f-[REDACTED]
STX_BINDING_SECRET_IAM_APIKEY_DESCRIPTION=Auto-generated for key 32d7766f-d7b8-4394-86fc-[REDACTED]
STX_BINDING_SECRET_APIKEY=[REDACTED]
STX_BINDING_SECRET_IAM_SERVICEID_CRN=crn:v1:bluemix:public:iam-identity::a/[REDACTED]::serviceid:ServiceId-426bb652-9860-4d51-a9d3-[REDACTED]
STX_BINDING_SECRET_IAM_APIKEY_NAME=my-speech-to-text-binding
STX_BINDING_CONFIGMAP_IAM_ROLE_CRN=crn:v1:bluemix:public:iam::::serviceRole:Manager
STX_BINDING_SECRET_URL=https://api.us-south.speech-to-text.watson.cloud.ibm.com/instances/0f97901f-37a9-4618-b07f-[REDACTED]
STX_BINDING_CONFIGMAP_IAM_APIKEY_DESCRIPTION=[REDACTED]
STX_URL=https://api.us-south.speech-to-text.watson.cloud.ibm.com/instances/0f97901f-37a9-4618-b07f-[REDACTED]
STX_BINDING_CONFIGMAP_APIKEY=[REDACTED]
``` 

sbose78: Thank you!
sbose78: /retest
sbose78: /retest

Patch:
diff --git a/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml b/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml
index 676a4306..67b31830 100644
--- a/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml
+++ b/deploy/crds/apps.openshift.io_servicebindingrequests_crd.yaml
@@ -314,6 +314,11 @@ spec:
             secret:
               description: Secret is the name of the intermediate secret
               type: string
+          required:
+          - applications
+          - bindingStatus
+          - conditions
+          - secret
           type: object
       type: object
   version: v1alpha1
diff --git a/hack/check-installplan.sh b/hack/check-installplan.sh
new file mode 100755
index 00000000..d90c8d47
--- /dev/null
+++ b/hack/check-installplan.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+function check_installplan () {
+    local install_plan=`kubectl get subscriptions service-binding-operator -n openshift-operators -o jsonpath='{.status.installPlanRef.name}'`
+
+    for i in  {1..120} ; do
+        if [[ ${install_plan} != "" ]] ; then
+            return 0
+        fi
+
+        sleep 3
+    done
+
+    return 1
+}
+
+
+echo "# Searching for install plan."
+
+if ! check_installplan ; then
+    echo "Install plan doesn't exist"
+    exit 1
+fi
+
+INSTALL_PLAN=`kubectl get subscriptions service-binding-operator -n openshift-operators -o jsonpath='{.status.installPlanRef.name}'`
+
+echo "Install plan found: ${INSTALL_PLAN}"
diff --git a/hack/check-operatorsource-pods.sh b/hack/check-operatorsource-pods.sh
new file mode 100755
index 00000000..4021f412
--- /dev/null
+++ b/hack/check-operatorsource-pods.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+function check_pod () {
+    local running_status=`kubectl get pods -n openshift-marketplace | grep "example-operators" | awk '{print $3}'`
+
+    for i in  {1..120} ; do
+        if [[ ${running_status} == "Running" ]] ; then
+            return 0
+        fi
+
+        sleep 3
+    done
+
+    return 1
+}
+
+
+echo "# Searching for operator source pod."
+
+if ! check_pod ; then
+    echo "Operator source pod is not running"
+    exit 1
+fi
+
+RUNNING_STATUS=`kubectl get pods -n openshift-marketplace | grep "example-operators" | awk '{print $3}'`
+
+echo "Operator source pod found with status: ${RUNNING_STATUS}"
diff --git a/hack/dev-release.sh b/hack/dev-release.sh
index 875e6efb..4fd3094e 100755
--- a/hack/dev-release.sh
+++ b/hack/dev-release.sh
@@ -12,10 +12,12 @@ sed -i -e "s,REPLACE_CSV_VERSION,service-binding-operator.v${BUNDLE_VERSION},g"
 kubectl apply -f ${OPERATOR_SOURCE}
 # Subscribing to the operator
 kubectl apply -f ${SUBSCRIPTION}
+./hack/check-operatorsource-pods.sh
 RUNNING_STATUS=`kubectl get pods -n openshift-marketplace | grep "example-operators" | awk '{print $3}'`
-if [ ${RUNNING_STATUS} = "Running" ] ; then
-	echo "Operator marketplace pod is running"
+if [ ${RUNNING_STATUS} != "Running" ] ; then
+	echo "Operator marketplace pod is not running"
 fi
+./hack/check-installplan.sh
 INSTALL_PLAN=`kubectl get subscriptions service-binding-operator -n openshift-operators -o jsonpath='{.status.installPlanRef.name}'`
 kubectl patch installplan ${INSTALL_PLAN} -n openshift-operators --type merge --patch '{"spec": {"approved": true}}'
 
diff --git a/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go b/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go
index 31b11993..f29f1a7f 100644
--- a/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go
+++ b/pkg/apis/apps/v1alpha1/servicebindingrequest_types.go
@@ -3,19 +3,16 @@ package v1alpha1
 import (
 	conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1"
 	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
 
 // ServiceBindingRequestSpec defines the desired state of ServiceBindingRequest
-// +k8s:openapi-gen=true
 type ServiceBindingRequestSpec struct {
 	// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
 	// Add custom validation using kubebuilder tags:
 	// 	https://book.kubebuilder.io/beyond_basics/generating_crd.html
-
 	// MountPathPrefix is the prefix for volume mount
 	// +optional
 	MountPathPrefix string `json:"mountPathPrefix,omitempty"`
@@ -49,24 +46,23 @@ type ServiceBindingRequestSpec struct {
 	// DetectBindingResources is flag used to bind all non-bindable variables from
 	// different subresources owned by backing operator CR.
 	// +optional
-	DetectBindingResources bool `json:"detectBindingResources"`
+	DetectBindingResources bool `json:"detectBindingResources,omitempty"`
 }
 
 // ServiceBindingRequestStatus defines the observed state of ServiceBindingRequest
 // +k8s:openapi-gen=true
 type ServiceBindingRequestStatus struct {
 	// BindingStatus is the status of the service binding request.
-	BindingStatus string `json:"bindingStatus,omitempty"`
+	BindingStatus string `json:"bindingStatus"`
 	// Conditions describes the state of the operator's reconciliation functionality.
-	Conditions []conditionsv1.Condition `json:"conditions,omitempty"`
+	Conditions []conditionsv1.Condition `json:"conditions"`
 	// Secret is the name of the intermediate secret
-	Secret string `json:"secret,omitempty"`
+	Secret string `json:"secret"`
 	// ApplicationObjects contains all the application objects filtered by label
-	ApplicationObjects []BoundApplication `json:"applications,omitempty"`
+	Applications []BoundApplication `json:"applications"`
 }
 
 // BackingServiceSelector defines the selector based on resource name, version, and resource kind
-// +k8s:openapi-gen=true
 type BackingServiceSelector struct {
 	metav1.GroupVersionKind `json:",inline"`
 	ResourceRef             string `json:"resourceRef"`
@@ -77,12 +73,11 @@ type BackingServiceSelector struct {
 // BoundApplication defines the application workloads to which the binding secret has
 // injected.
 type BoundApplication struct {
-	metav1.GroupVersionKind `json:",inline"`
-	v1.LocalObjectReference `json:",inline"`
+	metav1.GroupVersionKind     `json:",inline"`
+	corev1.LocalObjectReference `json:",inline"`
 }
 
 // ApplicationSelector defines the selector based on labels and GVR
-// +k8s:openapi-gen=true
 type ApplicationSelector struct {
 	// +optional
 	LabelSelector               *metav1.LabelSelector `json:"labelSelector,omitempty"`
@@ -95,8 +90,8 @@ type ApplicationSelector struct {
 // ServiceBindingRequest expresses intent to bind an operator-backed service with
 // an application workload.
 // +k8s:openapi-gen=true
-// +operator-sdk:gen-csv:customresourcedefinitions.displayName="Service Binding Request"
 // +kubebuilder:subresource:status
+// +operator-sdk:gen-csv:customresourcedefinitions.displayName="Service Binding Request"
 // +kubebuilder:resource:path=servicebindingrequests,shortName=sbr;sbrs
 type ServiceBindingRequest struct {
 	metav1.TypeMeta   `json:",inline"`
diff --git a/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go
index 17338809..e00095f5 100644
--- a/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go
@@ -1,5 +1,21 @@
 // +build !ignore_autogenerated
 
+/*
+Copyright The Kubernetes 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.
+*/
+
 // Code generated by operator-sdk. DO NOT EDIT.
 
 package v1alpha1
@@ -184,8 +200,8 @@ func (in *ServiceBindingRequestStatus) DeepCopyInto(out *ServiceBindingRequestSt
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
-	if in.ApplicationObjects != nil {
-		in, out := &in.ApplicationObjects, &out.ApplicationObjects
+	if in.Applications != nil {
+		in, out := &in.Applications, &out.Applications
 		*out = make([]BoundApplication, len(*in))
 		copy(*out, *in)
 	}
diff --git a/pkg/apis/apps/v1alpha1/zz_generated.openapi.go b/pkg/apis/apps/v1alpha1/zz_generated.openapi.go
index 3b903ade..086dbf67 100644
--- a/pkg/apis/apps/v1alpha1/zz_generated.openapi.go
+++ b/pkg/apis/apps/v1alpha1/zz_generated.openapi.go
@@ -1,5 +1,21 @@
 // +build !ignore_autogenerated
 
+/*
+Copyright The Kubernetes 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.
+*/
+
 // This file was autogenerated by openapi-gen. Do not edit it manually!
 
 package v1alpha1
@@ -11,103 +27,11 @@ import (
 
 func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
 	return map[string]common.OpenAPIDefinition{
-		"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.ApplicationSelector":         schema_pkg_apis_apps_v1alpha1_ApplicationSelector(ref),
-		"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.BackingServiceSelector":      schema_pkg_apis_apps_v1alpha1_BackingServiceSelector(ref),
 		"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.ServiceBindingRequest":       schema_pkg_apis_apps_v1alpha1_ServiceBindingRequest(ref),
-		"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.ServiceBindingRequestSpec":   schema_pkg_apis_apps_v1alpha1_ServiceBindingRequestSpec(ref),
 		"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.ServiceBindingRequestStatus": schema_pkg_apis_apps_v1alpha1_ServiceBindingRequestStatus(ref),
 	}
 }
 
-func schema_pkg_apis_apps_v1alpha1_ApplicationSelector(ref common.ReferenceCallback) common.OpenAPIDefinition {
-	return common.OpenAPIDefinition{
-		Schema: spec.Schema{
-			SchemaProps: spec.SchemaProps{
-				Description: "ApplicationSelector defines the selector based on labels and GVR",
-				Type:        []string{"object"},
-				Properties: map[string]spec.Schema{
-					"labelSelector": {
-						SchemaProps: spec.SchemaProps{
-							Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"),
-						},
-					},
-					"group": {
-						SchemaProps: spec.SchemaProps{
-							Type:   []string{"string"},
-							Format: "",
-						},
-					},
-					"version": {
-						SchemaProps: spec.SchemaProps{
-							Type:   []string{"string"},
-							Format: "",
-						},
-					},
-					"resource": {
-						SchemaProps: spec.SchemaProps{
-							Type:   []string{"string"},
-							Format: "",
-						},
-					},
-					"resourceRef": {
-						SchemaProps: spec.SchemaProps{
-							Type:   []string{"string"},
-							Format: "",
-						},
-					},
-				},
-				Required: []string{"group", "version", "resource"},
-			},
-		},
-		Dependencies: []string{
-			"k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"},
-	}
-}
-
-func schema_pkg_apis_apps_v1alpha1_BackingServiceSelector(ref common.ReferenceCallback) common.OpenAPIDefinition {
-	return common.OpenAPIDefinition{
-		Schema: spec.Schema{
-			SchemaProps: spec.SchemaProps{
-				Description: "BackingServiceSelector defines the selector based on resource name, version, and resource kind",
-				Type:        []string{"object"},
-				Properties: map[string]spec.Schema{
-					"group": {
-						SchemaProps: spec.SchemaProps{
-							Type:   []string{"string"},
-							Format: "",
-						},
-					},
-					"version": {
-						SchemaProps: spec.SchemaProps{
-							Type:   []string{"string"},
-							Format: "",
-						},
-					},
-					"kind": {
-						SchemaProps: spec.SchemaProps{
-							Type:   []string{"string"},
-							Format: "",
-						},
-					},
-					"resourceRef": {
-						SchemaProps: spec.SchemaProps{
-							Type:   []string{"string"},
-							Format: "",
-						},
-					},
-					"namespace": {
-						SchemaProps: spec.SchemaProps{
-							Type:   []string{"string"},
-							Format: "",
-						},
-					},
-				},
-				Required: []string{"group", "version", "kind", "resourceRef"},
-			},
-		},
-	}
-}
-
 func schema_pkg_apis_apps_v1alpha1_ServiceBindingRequest(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
@@ -152,80 +76,6 @@ func schema_pkg_apis_apps_v1alpha1_ServiceBindingRequest(ref common.ReferenceCal
 	}
 }
 
-func schema_pkg_apis_apps_v1alpha1_ServiceBindingRequestSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
-	return common.OpenAPIDefinition{
-		Schema: spec.Schema{
-			SchemaProps: spec.SchemaProps{
-				Description: "ServiceBindingRequestSpec defines the desired state of ServiceBindingRequest",
-				Type:        []string{"object"},
-				Properties: map[string]spec.Schema{
-					"mountPathPrefix": {
-						SchemaProps: spec.SchemaProps{
-							Description: "MountPathPrefix is the prefix for volume mount",
-							Type:        []string{"string"},
-							Format:      "",
-						},
-					},
-					"envVarPrefix": {
-						SchemaProps: spec.SchemaProps{
-							Description: "EnvVarPrefix is the prefix for environment variables",
-							Type:        []string{"string"},
-							Format:      "",
-						},
-					},
-					"customEnvVar": {
-						SchemaProps: spec.SchemaProps{
-							Description: "Custom env variables",
-							Type:        []string{"array"},
-							Items: &spec.SchemaOrArray{
-								Schema: &spec.Schema{
-									SchemaProps: spec.SchemaProps{
-										Ref: ref("k8s.io/api/core/v1.EnvVar"),
-									},
-								},
-							},
-						},
-					},
-					"backingServiceSelector": {
-						SchemaProps: spec.SchemaProps{
-							Description: "BackingServiceSelector is used to identify the backing service operator. Deprecation Notice: In the upcoming release, this field would be depcreated. It would be mandatory to set \"backingServiceSelectors\".",
-							Ref:         ref("github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.BackingServiceSelector"),
-						},
-					},
-					"backingServiceSelectors": {
-						SchemaProps: spec.SchemaProps{
-							Description: "BackingServiceSelectors is used to identify multiple backing services. This would be made a required field after 'BackingServiceSelector' is removed.",
-							Type:        []string{"array"},
-							Items: &spec.SchemaOrArray{
-								Schema: &spec.Schema{
-									SchemaProps: spec.SchemaProps{
-										Ref: ref("github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.BackingServiceSelector"),
-									},
-								},
-							},
-						},
-					},
-					"applicationSelector": {
-						SchemaProps: spec.SchemaProps{
-							Description: "ApplicationSelector is used to identify the application connecting to the backing service operator.",
-							Ref:         ref("github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.ApplicationSelector"),
-						},
-					},
-					"detectBindingResources": {
-						SchemaProps: spec.SchemaProps{
-							Description: "DetectBindingResources is flag used to bind all non-bindable variables from different subresources owned by backing operator CR.",
-							Type:        []string{"boolean"},
-							Format:      "",
-						},
-					},
-				},
-			},
-		},
-		Dependencies: []string{
-			"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.ApplicationSelector", "github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1.BackingServiceSelector", "k8s.io/api/core/v1.EnvVar"},
-	}
-}
-
 func schema_pkg_apis_apps_v1alpha1_ServiceBindingRequestStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
@@ -274,6 +124,7 @@ func schema_pkg_apis_apps_v1alpha1_ServiceBindingRequestStatus(ref common.Refere
 						},
 					},
 				},
+				Required: []string{"bindingStatus", "conditions", "secret", "applications"},
 			},
 		},
 		Dependencies: []string{
diff --git a/pkg/controller/servicebindingrequest/binding.go b/pkg/controller/servicebindingrequest/binding.go
index 32c8169d..ad024c15 100644
--- a/pkg/controller/servicebindingrequest/binding.go
+++ b/pkg/controller/servicebindingrequest/binding.go
@@ -283,7 +283,7 @@ func (b *ServiceBinder) setApplicationObjects(
 		}
 		boundApps = append(boundApps, boundApp)
 	}
-	sbrStatus.ApplicationObjects = boundApps
+	sbrStatus.Applications = boundApps
 }
 
 // buildPlan creates a new plan.
diff --git a/pkg/controller/servicebindingrequest/olm.go b/pkg/controller/servicebindingrequest/olm.go
index 460ed192..de71998c 100644
--- a/pkg/controller/servicebindingrequest/olm.go
+++ b/pkg/controller/servicebindingrequest/olm.go
@@ -42,7 +42,10 @@ func (o *OLM) listCSVs() ([]unstructured.Unstructured, error) {
 	gvr := olmv1alpha1.SchemeGroupVersion.WithResource(csvResource)
 	resourceClient := o.client.Resource(gvr).Namespace(o.ns)
 	csvs, err := resourceClient.List(metav1.ListOptions{})
-	if err != nil {
+	if err != nil && errors.IsNotFound(err) {
+		log.Warning("ClusterServiceVersions CRD is not installed")
+		return nil, nil
+	} else if err != nil {
 		log.Error(err, "during listing CSV objects from cluster")
 		return nil, err
 	}
diff --git a/pkg/controller/servicebindingrequest/sbrcontroller.go b/pkg/controller/servicebindingrequest/sbrcontroller.go
index 3b4efd66..bc61e287 100644
--- a/pkg/controller/servicebindingrequest/sbrcontroller.go
+++ b/pkg/controller/servicebindingrequest/sbrcontroller.go
@@ -5,6 +5,8 @@ import (
 	"strings"
 
 	olmv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
@@ -145,9 +147,18 @@ func (s *SBRController) AddWatchForGVK(gvk schema.GroupVersionKind) error {
 // addCSVWatch creates a watch on ClusterServiceVersion.
 func (s *SBRController) addCSVWatch() error {
 	log := s.logger
+	gvr := olmv1alpha1.SchemeGroupVersion.WithResource(ClusterServiceVersionKind)
+	resourceClient := s.Client.Resource(gvr).Namespace(os.Getenv("WATCH_NAMESPACE"))
+	_, err := resourceClient.List(metav1.ListOptions{})
+	if err != nil && errors.IsNotFound(err) {
+		log.Warning("ClusterServiceVersions CRD is not installed, skip watching")
+		return nil
+	} else if err != nil {
+		return err
+	}
 	csvGVK := olmv1alpha1.SchemeGroupVersion.WithKind(ClusterServiceVersionKind)
 	source := s.createSourceForGVK(csvGVK)
-	err := s.Controller.Watch(source, NewCreateWatchEventHandler(s))
+	err = s.Controller.Watch(source, NewCreateWatchEventHandler(s))
 	if err != nil {
 		return err
 	}
diff --git a/pkg/controller/servicebindingrequest/secret.go b/pkg/controller/servicebindingrequest/secret.go
index 17c181fa..3e7a382c 100644
--- a/pkg/controller/servicebindingrequest/secret.go
+++ b/pkg/controller/servicebindingrequest/secret.go
@@ -18,28 +18,6 @@ type Secret struct {
 	plan   *Plan             // plan instance
 }
 
-// customEnvParser parse informed data in order to interpolate with values provided by custom
-// environment component.
-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)
-	values, err := envParser.Parse()
-	if err != nil {
-		return nil, err
-	}
-
-	for k, v := range values {
-		data[k] = []byte(v.(string))
-	}
-	return data, nil
-}
-
 // buildResourceClient creates a resource client to handle corev1/secret resource.
 func (s *Secret) buildResourceClient() dynamic.ResourceInterface {
 	gvr := corev1.SchemeGroupVersion.WithResource(SecretResource)


Test patch:
diff --git a/pkg/controller/servicebindingrequest/olm_test.go b/pkg/controller/servicebindingrequest/olm_test.go
index 53c64398..f7146039 100644
--- a/pkg/controller/servicebindingrequest/olm_test.go
+++ b/pkg/controller/servicebindingrequest/olm_test.go
@@ -5,10 +5,15 @@ import (
 	"strings"
 	"testing"
 
+	olmv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
 	"github.com/stretchr/testify/require"
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/types"
+	k8stesting "k8s.io/client-go/testing"
 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
 
 	"github.com/redhat-developer/service-binding-operator/test/mocks"
@@ -27,6 +32,35 @@ func assertGVKs(t *testing.T, gvks []schema.GroupVersionKind) {
 	}
 }
 
+func TestOLMWithoutCSVCRD(t *testing.T) {
+	ns := "controller"
+	f := mocks.NewFake(t, ns)
+	client := f.FakeDynClient()
+	gvr := olmv1alpha1.SchemeGroupVersion.WithResource(csvResource)
+
+	// the original FakeDynClient would not return error for unknown resource
+	// prepend our reactor to mock a not found error like a real API server
+	client.PrependReactor("*", "*", func(action k8stesting.Action) (bool, runtime.Object, error) {
+		if gvr.String() == action.GetResource().String() {
+			return true, nil, errors.NewNotFound(gvr.GroupResource(), "the server could not find the requested resource")
+		}
+		return false, nil, nil
+	})
+	olm := NewOLM(client, ns)
+
+	t.Run("listCSVs without CSV CRD installed", func(t *testing.T) {
+		resourceClient := client.Resource(gvr).Namespace(ns)
+		objs, err := resourceClient.List(metav1.ListOptions{})
+		require.Error(t, err)
+		require.True(t, errors.IsNotFound(err))
+		require.Nil(t, objs)
+
+		csvs, err := olm.listCSVs()
+		require.NoError(t, err)
+		require.Len(t, csvs, 0)
+	})
+}
+
 func TestOLMNew(t *testing.T) {
 	ns := "controller"
 	csvName := "unit-csv"
diff --git a/pkg/controller/servicebindingrequest/reconciler_test.go b/pkg/controller/servicebindingrequest/reconciler_test.go
index 29aa7611..0239d605 100644
--- a/pkg/controller/servicebindingrequest/reconciler_test.go
+++ b/pkg/controller/servicebindingrequest/reconciler_test.go
@@ -93,7 +93,7 @@ func TestApplicationSelectorByName(t *testing.T) {
 		require.NoError(t, err)
 
 		require.Equal(t, BindingSuccess, sbrOutput.Status.BindingStatus)
-		require.Equal(t, 1, len(sbrOutput.Status.ApplicationObjects))
+		require.Equal(t, 1, len(sbrOutput.Status.Applications))
 		expectedStatus := v1alpha1.BoundApplication{
 			GroupVersionKind: v1.GroupVersionKind{
 				Group:   deploymentsGVR.Group,
@@ -104,7 +104,7 @@ func TestApplicationSelectorByName(t *testing.T) {
 				Name: namespacedName.Name,
 			},
 		}
-		require.True(t, reflect.DeepEqual(expectedStatus, sbrOutput.Status.ApplicationObjects[0]))
+		require.True(t, reflect.DeepEqual(expectedStatus, sbrOutput.Status.Applications[0]))
 	})
 }
 
@@ -151,7 +151,7 @@ func TestReconcilerReconcileUsingSecret(t *testing.T) {
 		require.Equal(t, "BindingSuccess", sbrOutput.Status.BindingStatus)
 		require.Equal(t, reconcilerName, sbrOutput.Status.Secret)
 
-		require.Equal(t, 1, len(sbrOutput.Status.ApplicationObjects))
+		require.Equal(t, 1, len(sbrOutput.Status.Applications))
 		expectedStatus := v1alpha1.BoundApplication{
 			GroupVersionKind: v1.GroupVersionKind{
 				Group:   deploymentsGVR.Group,
@@ -162,7 +162,7 @@ func TestReconcilerReconcileUsingSecret(t *testing.T) {
 				Name: namespacedName.Name,
 			},
 		}
-		require.True(t, reflect.DeepEqual(expectedStatus, sbrOutput.Status.ApplicationObjects[0]))
+		require.True(t, reflect.DeepEqual(expectedStatus, sbrOutput.Status.Applications[0]))
 	})
 }
 
@@ -234,7 +234,7 @@ func TestReconcilerGenericBinding(t *testing.T) {
 
 	require.Equal(t, "BindingFail", sbrOutput.Status.BindingStatus)
 	require.Equal(t, corev1.ConditionFalse, sbrOutput.Status.Conditions[0].Status)
-	require.Equal(t, 0, len(sbrOutput.Status.ApplicationObjects))
+	require.Equal(t, 0, len(sbrOutput.Status.Applications))
 
 	// Reconcile with deployment
 	f.AddMockedUnstructuredDeployment(reconcilerName, matchLabels)
@@ -254,7 +254,7 @@ func TestReconcilerGenericBinding(t *testing.T) {
 	require.Equal(t, "BindingSuccess", sbrOutput2.Status.BindingStatus)
 	require.Equal(t, reconcilerName, sbrOutput2.Status.Secret)
 	require.Equal(t, corev1.ConditionTrue, sbrOutput2.Status.Conditions[0].Status)
-	require.Equal(t, 1, len(sbrOutput2.Status.ApplicationObjects))
+	require.Equal(t, 1, len(sbrOutput2.Status.Applications))
 
 	// Update Credentials
 	s := corev1.Secret{}
@@ -278,7 +278,7 @@ func TestReconcilerGenericBinding(t *testing.T) {
 	require.Equal(t, corev1.ConditionTrue, sbrOutput3.Status.Conditions[0].Status)
 	require.Equal(t, reconcilerName, sbrOutput3.Status.Secret)
 	require.Equal(t, s.Data["password"], []byte("abc123"))
-	require.Equal(t, 1, len(sbrOutput3.Status.ApplicationObjects))
+	require.Equal(t, 1, len(sbrOutput3.Status.Applications))
 }
 
 //TestReconcilerReconcileWithConflictingAppSelc tests when sbr has conflicting ApplicationSel such as MatchLabels=App1 and ResourceRef=App2 it should prioritise the ResourceRef
@@ -328,6 +328,6 @@ func TestReconcilerReconcileWithConflictingAppSelc(t *testing.T) {
 		require.Equal(t, BindingSuccess, sbrOutput.Status.BindingStatus)
 		require.Equal(t, reconcilerName, sbrOutput.Status.Secret)
 		require.Equal(t, corev1.ConditionTrue, sbrOutput.Status.Conditions[0].Status)
-		require.True(t, reflect.DeepEqual(expectedStatus, sbrOutput.Status.ApplicationObjects[0]))
+		require.True(t, reflect.DeepEqual(expectedStatus, sbrOutput.Status.Applications[0]))
 	})
 }
diff --git a/pkg/controller/servicebindingrequest/secret_test.go b/pkg/controller/servicebindingrequest/secret_test.go
index 6ae996e2..6562520e 100644
--- a/pkg/controller/servicebindingrequest/secret_test.go
+++ b/pkg/controller/servicebindingrequest/secret_test.go
@@ -32,12 +32,6 @@ func TestSecretNew(t *testing.T) {
 
 	s := NewSecret(f.FakeDynClient(), plan)
 
-	t.Run("customEnvParser", func(t *testing.T) {
-		customData, err := s.customEnvParser(data)
-		assert.NoError(t, err)
-		assert.NotNil(t, customData)
-	})
-
 	t.Run("createOrUpdate", func(t *testing.T) {
 		u, err := s.createOrUpdate(data)
 		assert.NoError(t, err)