Repository: redhat-developer/service-binding-operator
Pull request #698 (2020-10-01 09:08:09)

Make services a required field in sbr spec
### Motivation

https://issues.redhat.com/browse/APPSVC-696

### Testing

`make test-acceptance VERBOSE=3`

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 **dhritishikhar** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @dhritishikhar` 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":["dhritishikhar"]} -->
openshift-ci-robot: Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with `/test all`
pratikjagrut: /test all
openshift-ci-robot: @pratikjagrut: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-lint | 82bfc8c4661890d39f5f7c69e4eed8ec5b546571 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-lint/1311594115175026688) | `/test 4.5-lint`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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 -->
pratikjagrut: /test all
openshift-ci-robot: @pratikjagrut: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-acceptance | 1ce44d72a9dc0d16f3a3f0c7c77b2b6ed4318768 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-acceptance/1311608137949646848) | `/test 4.5-acceptance`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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 -->
Avni-Sharma: /test 4.5-acceptance
Avni-Sharma: /lgtm
pedjak: /hold

openshift-ci-robot: New changes are detected. LGTM label has been removed.
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** after the PR has been reviewed.
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: @pratikjagrut: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-lint | f3ba54cb500e65311dbf2557076649a32974f39f | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-lint/1311770566419550208) | `/test 4.5-lint`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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 -->
pedjak: /hold cancel
openshift-ci-robot: @pratikjagrut: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-unit | 8ff387fa27c20fbac5ea4ebbaf8dc811ab3dc005 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-unit/1312440945576251392) | `/test 4.5-unit`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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: @pratikjagrut: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-unit | 8ff387fa27c20fbac5ea4ebbaf8dc811ab3dc005 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-unit/1312440945576251392) | `/test 4.5-unit`
ci/prow/4.5-lint | 8ff387fa27c20fbac5ea4ebbaf8dc811ab3dc005 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-lint/1312440945538502656) | `/test 4.5-lint`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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: @pratikjagrut: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-unit | 8ff387fa27c20fbac5ea4ebbaf8dc811ab3dc005 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-unit/1312440945576251392) | `/test 4.5-unit`
ci/prow/4.5-lint | 8ff387fa27c20fbac5ea4ebbaf8dc811ab3dc005 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-lint/1312440945538502656) | `/test 4.5-lint`
ci/prow/4.5-acceptance | 8ff387fa27c20fbac5ea4ebbaf8dc811ab3dc005 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-acceptance/1312440945471393792) | `/test 4.5-acceptance`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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: @pratikjagrut: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-acceptance | 59eea9ef90be1da57718a6b1a38920d14aaeffa3 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-acceptance/1312461022816112640) | `/test 4.5-acceptance`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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: @pratikjagrut: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-acceptance | 7ffc1dda5d52eb83fd9dc15d25086e6306591a57 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-acceptance/1312629752602824704) | `/test 4.5-acceptance`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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: @pratikjagrut: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-acceptance | 37c3863781f0c270c81337fbd354cb9500a8c249 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-acceptance/1312649813090635776) | `/test 4.5-acceptance`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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: @pratikjagrut: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-acceptance | dd3b4479ae61cbd2cd156684a5e6f5db049c8493 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/698/pull-ci-redhat-developer-service-binding-operator-master-4.5-acceptance/1313043078965104640) | `/test 4.5-acceptance`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=698). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:pratikjagrut).

<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 -->
pratikjagrut: /test 4.5-acceptance
pedjak: /lgtm
/approve
openshift-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/redhat-developer/service-binding-operator/pull/698#issuecomment-703697474" title="Approved">pedjak</a>*

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).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

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

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

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":[]} -->

Patch:
diff --git a/deploy/crds/operators.coreos.com_servicebindings_crd.yaml b/deploy/crds/operators.coreos.com_servicebindings_crd.yaml
index 1a3a3cf6..0cbbdd3c 100644
--- a/deploy/crds/operators.coreos.com_servicebindings_crd.yaml
+++ b/deploy/crds/operators.coreos.com_servicebindings_crd.yaml
@@ -257,7 +257,10 @@ spec:
                 - kind
                 - version
                 type: object
+              minItems: 1
               type: array
+          required:
+          - services
           type: object
         status:
           description: ServiceBindingStatus defines the observed state of ServiceBinding
diff --git a/pkg/apis/operators/v1alpha1/servicebinding_types.go b/pkg/apis/operators/v1alpha1/servicebinding_types.go
index fbb020ab..54fc7fe9 100644
--- a/pkg/apis/operators/v1alpha1/servicebinding_types.go
+++ b/pkg/apis/operators/v1alpha1/servicebinding_types.go
@@ -23,7 +23,8 @@ type ServiceBindingSpec struct {
 	CustomEnvVar []corev1.EnvVar `json:"customEnvVar,omitempty"`
 
 	// Services is used to identify multiple backing services.
-	Services *[]Service `json:"services,omitempty"`
+	// +kubebuilder:validation:MinItems:=1
+	Services []Service `json:"services"`
 
 	// Application is used to identify the application connecting to the
 	// backing service operator.
diff --git a/pkg/apis/operators/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/operators/v1alpha1/zz_generated.deepcopy.go
index b1482d3a..62871307 100644
--- a/pkg/apis/operators/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/operators/v1alpha1/zz_generated.deepcopy.go
@@ -195,13 +195,9 @@ func (in *ServiceBindingSpec) DeepCopyInto(out *ServiceBindingSpec) {
 	}
 	if in.Services != nil {
 		in, out := &in.Services, &out.Services
-		*out = new([]Service)
-		if **in != nil {
-			in, out := *in, *out
-			*out = make([]Service, len(*in))
-			for i := range *in {
-				(*in)[i].DeepCopyInto(&(*out)[i])
-			}
+		*out = make([]Service, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
 	if in.Application != nil {
diff --git a/pkg/controller/servicebinding/mapper.go b/pkg/controller/servicebinding/mapper.go
index 068b3b8c..b48a4172 100644
--- a/pkg/controller/servicebinding/mapper.go
+++ b/pkg/controller/servicebinding/mapper.go
@@ -42,8 +42,7 @@ func isSecret(obj runtime.Object) bool {
 
 // isSBRService checks whether the given obj is a service in given sbr.
 func isSBRService(sbr *v1alpha1.ServiceBinding, obj runtime.Object) bool {
-	services := servicesOrEmptyList(sbr.Spec.Services)
-	for _, svc := range services {
+	for _, svc := range sbr.Spec.Services {
 		svcGVK := schema.GroupVersionKind{Group: svc.Group, Version: svc.Version, Kind: svc.Kind}
 		if obj.GetObjectKind().GroupVersionKind() == svcGVK {
 			return true
diff --git a/pkg/controller/servicebinding/reconciler.go b/pkg/controller/servicebinding/reconciler.go
index 27919e3a..40f5f834 100644
--- a/pkg/controller/servicebinding/reconciler.go
+++ b/pkg/controller/servicebinding/reconciler.go
@@ -78,14 +78,6 @@ func (r *reconciler) getServiceBinding(
 	return sbr, nil
 }
 
-func servicesOrEmptyList(maybeServices *[]v1alpha1.Service) []v1alpha1.Service {
-	services := []v1alpha1.Service{}
-	if maybeServices != nil {
-		services = *maybeServices
-	}
-	return services
-}
-
 // Reconcile a ServiceBinding by the following steps:
 // 1. Inspecting SBR in order to identify backend service. The service is composed by a CRD name and
 //    kind, and by inspecting "connects-to" label identify the name of service instance;
@@ -128,9 +120,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err
 
 	ctx := context.Background()
 
-	selectors := servicesOrEmptyList(sbr.Spec.Services)
-
-	if len(selectors) == 0 {
+	if len(sbr.Spec.Services) == 0 {
 		_, updateErr := updateServiceBindingStatus(
 			r.dynClient,
 			sbr,
@@ -162,7 +152,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err
 		logger.WithName("buildServiceContexts"),
 		r.dynClient,
 		sbr.GetNamespace(),
-		selectors,
+		sbr.Spec.Services,
 		sbr.Spec.DetectBindingResources,
 		r.restMapper,
 	)


Test patch:
diff --git a/pkg/controller/servicebinding/mapper_test.go b/pkg/controller/servicebinding/mapper_test.go
index b3f1b65e..4791d424 100644
--- a/pkg/controller/servicebinding/mapper_test.go
+++ b/pkg/controller/servicebinding/mapper_test.go
@@ -1,4 +1,4 @@
-	package servicebinding
+package servicebinding
 
 import (
 	"testing"
@@ -35,7 +35,7 @@ func TestSBRRequestMapperMap(t *testing.T) {
 				},
 				LocalObjectReference: corev1.LocalObjectReference{Name: "mapper-unit-deployment"},
 			},
-			Services: &[]v1alpha1.Service{
+			Services: []v1alpha1.Service{
 				{
 					GroupVersionKind: metav1.GroupVersionKind{
 						Group:   "",
diff --git a/pkg/controller/servicebinding/sbrcontroller_test.go b/pkg/controller/servicebinding/sbrcontroller_test.go
index 6692091e..89a8c1db 100644
--- a/pkg/controller/servicebinding/sbrcontroller_test.go
+++ b/pkg/controller/servicebinding/sbrcontroller_test.go
@@ -35,7 +35,7 @@ func TestSBRControllerBuildSBRPredicate(t *testing.T) {
 	t.Run("update", func(t *testing.T) {
 		sbrA := &v1alpha1.ServiceBinding{
 			Spec: v1alpha1.ServiceBindingSpec{
-				Services: &[]v1alpha1.Service{
+				Services: []v1alpha1.Service{
 					{
 						GroupVersionKind:     metav1.GroupVersionKind{Group: "test", Version: "v1alpha1", Kind: "TestHost"},
 						LocalObjectReference: corev1.LocalObjectReference{Name: ""},
@@ -45,7 +45,7 @@ func TestSBRControllerBuildSBRPredicate(t *testing.T) {
 		}
 		sbrB := &v1alpha1.ServiceBinding{
 			Spec: v1alpha1.ServiceBindingSpec{
-				Services: &[]v1alpha1.Service{
+				Services: []v1alpha1.Service{
 					{
 						GroupVersionKind:     metav1.GroupVersionKind{Group: "test", Version: "v1", Kind: "TestHost"},
 						LocalObjectReference: corev1.LocalObjectReference{Name: ""},
diff --git a/pkg/controller/servicebinding/servicebinder_test.go b/pkg/controller/servicebinding/servicebinder_test.go
index 16f19cd8..94b734a7 100644
--- a/pkg/controller/servicebinding/servicebinder_test.go
+++ b/pkg/controller/servicebinding/servicebinder_test.go
@@ -220,7 +220,7 @@ func TestServiceBinder_Bind(t *testing.T) {
 				},
 				LocalObjectReference: corev1.LocalObjectReference{Name: d.GetName()},
 			},
-			Services: &[]v1alpha1.Service{
+			Services: []v1alpha1.Service{
 				{
 					GroupVersionKind: metav1.GroupVersionKind{
 						Group:   db1.GetObjectKind().GroupVersionKind().Group,
@@ -255,7 +255,7 @@ func TestServiceBinder_Bind(t *testing.T) {
 				},
 				LocalObjectReference: corev1.LocalObjectReference{Name: d.GetName()},
 			},
-			Services: &[]v1alpha1.Service{
+			Services: []v1alpha1.Service{
 				{
 					GroupVersionKind: metav1.GroupVersionKind{
 						Group:   db1.GetObjectKind().GroupVersionKind().Group,
@@ -301,7 +301,7 @@ func TestServiceBinder_Bind(t *testing.T) {
 				},
 				LocalObjectReference: corev1.LocalObjectReference{Name: d.GetName()},
 			},
-			Services: &[]v1alpha1.Service{
+			Services: []v1alpha1.Service{
 				{
 					GroupVersionKind: metav1.GroupVersionKind{
 						Group:   db1.GetObjectKind().GroupVersionKind().Group,
@@ -336,7 +336,7 @@ func TestServiceBinder_Bind(t *testing.T) {
 			Application: &v1alpha1.Application{
 				LabelSelector: &metav1.LabelSelector{},
 			},
-			Services: &[]v1alpha1.Service{
+			Services: []v1alpha1.Service{
 				{
 					GroupVersionKind: metav1.GroupVersionKind{
 						Group:   db1.GetObjectKind().GroupVersionKind().Group,
@@ -371,7 +371,7 @@ func TestServiceBinder_Bind(t *testing.T) {
 				},
 				LocalObjectReference: corev1.LocalObjectReference{Name: d.GetName()},
 			},
-			Services: &[]v1alpha1.Service{},
+			Services: []v1alpha1.Service{},
 		},
 		Status: v1alpha1.ServiceBindingStatus{},
 	}
diff --git a/pkg/controller/servicebinding/servicecontext_test.go b/pkg/controller/servicebinding/servicecontext_test.go
index f62c0ecd..ba3eabcb 100644
--- a/pkg/controller/servicebinding/servicecontext_test.go
+++ b/pkg/controller/servicebinding/servicecontext_test.go
@@ -1,9 +1,10 @@
 package servicebinding
 
 import (
-	corev1 "k8s.io/api/core/v1"
 	"testing"
 
+	corev1 "k8s.io/api/core/v1"
+
 	routev1 "github.com/openshift/api/route/v1"
 	pgv1alpha1 "github.com/operator-backing-service-samples/postgresql-operator/pkg/apis/postgresql/v1alpha1"
 	"github.com/redhat-developer/service-binding-operator/pkg/apis/operators/v1alpha1"
@@ -49,7 +50,7 @@ func TestBuildServiceContexts(t *testing.T) {
 		sbr := f.AddMockedServiceBinding(sbrName, nil, firstResourceRef, "", deploymentsGVR, matchLabels)
 
 		serviceCtxs, err := buildServiceContexts(
-			logger, f.FakeDynClient(), firstNamespace, *sbr.Spec.Services, &falseBool, restMapper)
+			logger, f.FakeDynClient(), firstNamespace, sbr.Spec.Services, &falseBool, restMapper)
 
 		require.NoError(t, err, "buildServiceContexts must execute without errors")
 		require.Len(t, serviceCtxs, 1, "buildServiceContexts must return only one item")
@@ -97,7 +98,7 @@ func TestBuildServiceContexts(t *testing.T) {
 		sbrName := "services-in-different-ns"
 
 		sbr := f.AddMockedServiceBinding(sbrName, &sameNs, sameNsResourceRef, "", deploymentsGVR, matchLabels)
-		sbr.Spec.Services = &[]v1alpha1.Service{
+		sbr.Spec.Services = []v1alpha1.Service{
 			{
 				GroupVersionKind: metav1.GroupVersionKind{
 					Group:   mocks.CRDName,
@@ -106,7 +107,7 @@ func TestBuildServiceContexts(t *testing.T) {
 				},
 
 				LocalObjectReference: corev1.LocalObjectReference{Name: otherNsResourceRef},
-				Namespace: &otherNs,
+				Namespace:            &otherNs,
 			},
 			{
 				GroupVersionKind: metav1.GroupVersionKind{
@@ -115,12 +116,12 @@ func TestBuildServiceContexts(t *testing.T) {
 					Kind:    mocks.CRDKind,
 				},
 				LocalObjectReference: corev1.LocalObjectReference{Name: sameNsResourceRef},
-				Namespace: &sameNs,
+				Namespace:            &sameNs,
 			},
 		}
 
 		serviceCtxs, err := buildServiceContexts(
-			logger, f.FakeDynClient(), sameNs, *sbr.Spec.Services, &falseBool, restMapper)
+			logger, f.FakeDynClient(), sameNs, sbr.Spec.Services, &falseBool, restMapper)
 
 		require.NoError(t, err, "buildServiceContexts must execute without errors")
 		require.Len(t, serviceCtxs, 2, "buildServiceContexts must return both service contexts")
diff --git a/test/acceptance/features/bindAppToService.feature b/test/acceptance/features/bindAppToService.feature
index a78d4eec..3451139d 100644
--- a/test/acceptance/features/bindAppToService.feature
+++ b/test/acceptance/features/bindAppToService.feature
@@ -670,3 +670,61 @@ Feature: Bind an application to a service
         And The application env var "BACKEND_SPEC_DBNAME" has value "db-demo"
         And jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding "rsa-2" should be changed to "True"
         And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding "rsa-2" should be changed to "True"
+
+        @negative
+        Scenario: Service Binding with empty services is not allowed in the cluster
+        When Invalid Service Binding is applied
+            """
+            apiVersion: operators.coreos.com/v1alpha1
+            kind: ServiceBinding
+            metadata:
+                name: binding-request-empty-services
+            spec:
+                services:
+            """
+        Then Error message "invalid: spec.services: Invalid value: \"null\"" is thrown
+        And Service Binding "binding-request-empty-services" is not persistent in the cluster
+
+        @negative
+        Scenario: Service Binding without gvk of services is not allowed in the cluster
+        When Invalid Service Binding is applied
+            """
+            apiVersion: operators.coreos.com/v1alpha1
+            kind: ServiceBinding
+            metadata:
+                name: binding-request-without-gvk
+            spec:
+                services:
+                -   name: backend-demo
+            """
+        Then Error message "spec.services.group: Required value" is thrown
+        And Error message "spec.services.kind: Required value" is thrown
+        And Error message "spec.services.version: Required value" is thrown
+        And Service Binding "binding-request-without-gvk" is not persistent in the cluster
+
+        @negative
+        Scenario: Removing service from services field from existing serivce binding is not allowed
+        Given Service Binding is applied
+            """
+            apiVersion: operators.coreos.com/v1alpha1
+            kind: ServiceBinding
+            metadata:
+                name: binding-request-remove-service
+            spec:
+                services:
+                -   group: service.example.com
+                    version: v1
+                    kind: Backserv
+                    name: demo-backserv-cr-2
+            """
+        When Invalid Service Binding is applied
+            """
+            apiVersion: operators.coreos.com/v1alpha1
+            kind: ServiceBinding
+            metadata:
+                name: binding-request-remove-service
+            spec:
+                services:
+            """
+        Then Error message "invalid: spec.services: Required value" is thrown
+        And Service Binding "binding-request-remove-service" is not updated
diff --git a/test/acceptance/features/steps/service_binding.py b/test/acceptance/features/steps/service_binding.py
index aece6eaf..6c1ec949 100644
--- a/test/acceptance/features/steps/service_binding.py
+++ b/test/acceptance/features/steps/service_binding.py
@@ -8,5 +8,11 @@ class ServiceBinding(object):
     openshift = Openshift()
 
     def create(self, yaml):
-        sbr_create_output = self.openshift.oc_apply(yaml)
-        return re.search(r'.*servicebinding.operators.coreos.com/.*(created|unchanged)', sbr_create_output)
+        return re.search(r'.*servicebinding.operators.coreos.com/.*(created|unchanged)', self.attempt_to_create(yaml))
+
+    def attempt_to_create(self, yaml):
+        return self.openshift.oc_apply(yaml)
+
+    def get_servicebinding_info_by_jsonpath(self, servicebinding_name, namespace, json_path):
+        openshift = Openshift()
+        return openshift.get_resource_info_by_jsonpath("servicebinding", servicebinding_name, namespace, json_path)
diff --git a/test/acceptance/features/steps/steps.py b/test/acceptance/features/steps/steps.py
index 74c0bc81..c25407a6 100644
--- a/test/acceptance/features/steps/steps.py
+++ b/test/acceptance/features/steps/steps.py
@@ -174,8 +174,11 @@ sbr_is_applied_step = u'Service Binding is applied'
 
 @given(sbr_is_applied_step)
 @when(sbr_is_applied_step)
+@then(sbr_is_applied_step)
 def sbr_is_applied(context):
     sbr_yaml = context.text
+    metadata_name = re.sub(r'.*: ', '', re.search(r'name: .*', sbr_yaml).group(0))
+    context.sbr_name = metadata_name
     sbr = ServiceBinding()
     if context.__contains__("application") and context.__contains__("application_type"):
         application = context.application
@@ -394,3 +397,40 @@ def etc_cluster_is_running(context, etcd_name):
         print("etcd cluster not present, creating etcd cluster")
         assert etcd_cluster.create() is True, "etcd cluster is not created"
         assert etcd_cluster.is_present() is True, "etcd cluster is not present"
+
+
+@when(u'Invalid Service Binding is applied')
+def invalid_sbr_is_applied(context):
+    sbr = ServiceBinding()
+    # Get resource version of sbr if sbr is available
+    if context.__contains__("sbr_name"):
+        json_path = "{.metadata.resourceVersion}"
+        rv = sbr.get_servicebinding_info_by_jsonpath(context.sbr_name, context.namespace.name, json_path)
+        context.resource_version = rv
+    yaml = context.text
+    output = sbr.attempt_to_create(yaml)
+    context.expected_error = output
+
+
+@then(u'Error message "{err_msg}" is thrown')
+def validate_error(context, err_msg):
+    search = re.search(rf'.*{err_msg}.*', context.expected_error)
+    assert search is not None, "Actual error: {context.expected_error}, Expected error: {err_msg}"
+
+
+@then(u'Service Binding "{sb_name}" is not persistent in the cluster')
+def get_sb(context, sb_name):
+    openshift = Openshift()
+    output = openshift.search_resource_in_namespace("servicebindings", sb_name, context.namespace.name)
+    assert output is None, "Service Binding {sb_name} is present in namespace {context.namespace.name}"
+
+
+@then(u'Service Binding "{sb_name}" is not updated')
+def get_persistent_sb(context, sb_name):
+    openshift = Openshift()
+    json_path = "{.metadata.resourceVersion}"
+    output = openshift.get_resource_info_by_jsonpath("servicebindings", sb_name, context.namespace.name, json_path)
+    if output == context.resource_version:
+        assert True
+    else:
+        assert False, "Service Binding got updated"
diff --git a/test/mocks/mocks.go b/test/mocks/mocks.go
index 36655efa..36923683 100644
--- a/test/mocks/mocks.go
+++ b/test/mocks/mocks.go
@@ -437,15 +437,15 @@ func MultiNamespaceServiceBindingMock(
 			CustomEnvVar:    []corev1.EnvVar{},
 			Application: &v1alpha1.Application{
 				GroupVersionResource: metav1.GroupVersionResource{Group: applicationGVR.Group, Version: applicationGVR.Version, Resource: applicationGVR.Resource},
-				LocalObjectReference:                 corev1.LocalObjectReference{Name: applicationResourceRef},
+				LocalObjectReference: corev1.LocalObjectReference{Name: applicationResourceRef},
 				LabelSelector:        &metav1.LabelSelector{MatchLabels: matchLabels},
 			},
 			DetectBindingResources: &falseBoolPtr,
-			Services: &[]v1alpha1.Service{
+			Services: []v1alpha1.Service{
 				{
-					GroupVersionKind: metav1.GroupVersionKind{Group: CRDName, Version: CRDVersion, Kind: CRDKind},
-					LocalObjectReference:             corev1.LocalObjectReference{Name: backingServiceResourceRef},
-					Namespace:        &backingServiceNamespace,
+					GroupVersionKind:     metav1.GroupVersionKind{Group: CRDName, Version: CRDVersion, Kind: CRDKind},
+					LocalObjectReference: corev1.LocalObjectReference{Name: backingServiceResourceRef},
+					Namespace:            &backingServiceNamespace,
 				},
 			},
 		},
@@ -477,15 +477,15 @@ func ServiceBindingMock(
 			CustomEnvVar:    []corev1.EnvVar{},
 			Application: &v1alpha1.Application{
 				GroupVersionResource: metav1.GroupVersionResource{Group: applicationGVR.Group, Version: applicationGVR.Version, Resource: applicationGVR.Resource},
-				LocalObjectReference:                 corev1.LocalObjectReference{Name: applicationResourceRef},
+				LocalObjectReference: corev1.LocalObjectReference{Name: applicationResourceRef},
 				LabelSelector:        &metav1.LabelSelector{MatchLabels: matchLabels},
 			},
 			DetectBindingResources: &falseBoolPtr,
-			Services: &[]v1alpha1.Service{
+			Services: []v1alpha1.Service{
 				{
-					GroupVersionKind: metav1.GroupVersionKind{Group: CRDName, Version: CRDVersion, Kind: CRDKind},
-					LocalObjectReference:             corev1.LocalObjectReference{Name: backingServiceResourceRef},
-					Namespace:        backingServiceNamespace,
+					GroupVersionKind:     metav1.GroupVersionKind{Group: CRDName, Version: CRDVersion, Kind: CRDKind},
+					LocalObjectReference: corev1.LocalObjectReference{Name: backingServiceResourceRef},
+					Namespace:            backingServiceNamespace,
 				},
 			},
 		},