diff --git a/pkg/reconcile/pipeline/context/service/crd_test.go b/pkg/reconcile/pipeline/context/service/crd_test.go index c2e8faed5f..bc2faf86b8 100644 --- a/pkg/reconcile/pipeline/context/service/crd_test.go +++ b/pkg/reconcile/pipeline/context/service/crd_test.go @@ -4,7 +4,6 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" - "github.com/redhat-developer/service-binding-operator/pkg/binding" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic/fake" @@ -31,17 +30,6 @@ var _ = Describe("CRD", func() { mockCtrl.Finish() }) - It("should be bindable if marked as provisioned service", func() { - u := &unstructured.Unstructured{} - annotations := map[string]string{ - binding.ProvisionedServiceAnnotationKey: "true", - } - u.SetAnnotations(annotations) - crd := &customResourceDefinition{resource: u, client: client} - - Expect(crd.IsBindable()).To(BeTrue()) - }) - DescribeTable("should be bindable if has binding annotation", func(annKey string) { u := &unstructured.Unstructured{} annotations := map[string]string{ @@ -70,4 +58,76 @@ var _ = Describe("CRD", func() { crd := &customResourceDefinition{resource: u, client: client} Expect(crd.IsBindable()).To(BeFalse()) }) + + It("should be bindable if the status has binding.name attribute", func() { + u := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "kind": "BackingService", + "apiVersion": "app1.example.org/v1alpha1", + "metadata": map[string]interface{}{ + "name": "back1", + }, + "spec": map[string]interface{}{ + "versions": []map[string]interface{}{ + { + "schema": map[string]interface{}{ + "openAPIV3Schema": map[string]interface{}{ + "properties": map[string]interface{}{ + "status": map[string]interface{}{ + "properties": map[string]interface{}{ + "binding": map[string]interface{}{ + "properties": map[string]interface{}{ + "name": map[string]interface{}{ + "type": "string", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }} + crd := &customResourceDefinition{resource: u, client: client} + Expect(crd.IsBindable()).To(BeTrue()) + }) + It("should not be bindable if the status has binding.name with attribute value", func() { + u := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "kind": "BackingService", + "apiVersion": "app1.example.org/v1alpha1", + "metadata": map[string]interface{}{ + "name": "back1", + }, + "spec": map[string]interface{}{ + "versions": []map[string]interface{}{ + { + "schema": map[string]interface{}{ + "openAPIV3Schema": map[string]interface{}{ + "properties": map[string]interface{}{ + "status": map[string]interface{}{ + "properties": map[string]interface{}{ + "binding": map[string]interface{}{ + "properties": map[string]interface{}{ + "name": map[string]interface{}{ + "type": "not-string", // correct: string + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }} + crd := &customResourceDefinition{resource: u, client: client} + Expect(crd.IsBindable()).To(BeFalse()) + }) + }) diff --git a/pkg/reconcile/pipeline/context/service/service.go b/pkg/reconcile/pipeline/context/service/service.go index 423e988873..7c3900570d 100644 --- a/pkg/reconcile/pipeline/context/service/service.go +++ b/pkg/reconcile/pipeline/context/service/service.go @@ -2,20 +2,21 @@ package service import ( "context" + "reflect" + "strings" "github.com/redhat-developer/service-binding-operator/pkg/binding" "github.com/redhat-developer/service-binding-operator/pkg/binding/registry" "github.com/redhat-developer/service-binding-operator/pkg/client/kubernetes" "github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline" - "reflect" - "github.com/redhat-developer/service-binding-operator/pkg/util" "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/schema" "k8s.io/client-go/dynamic" + "k8s.io/client-go/util/jsonpath" ) var _ pipeline.Service = &service{} @@ -234,17 +235,45 @@ func (c *customResourceDefinition) kind() string { return "" } +// getValuesByJSONPath returns values from the given map matching the provided JSONPath +// 'path' argument takes JSONPath expressions enclosed by curly braces {} +// see https://kubernetes.io/docs/reference/kubectl/jsonpath/ for more details +// It returns zero or more filtered values back, +// or error if the jsonpath is invalid or it cannot be applied on the given map +func getValuesByJSONPath(obj map[string]interface{}, path string) ([]reflect.Value, error) { + j := jsonpath.New("") + err := j.Parse(path) + if err != nil { + return nil, err + } + result, err := j.FindResults(obj) + if err != nil { + return nil, err + } + if len(result) > 1 { + w := strings.Builder{} + for i := range result { + if err := j.PrintResults(&w, result[i]); err != nil { + return nil, err + } + } + return []reflect.Value{reflect.ValueOf(w.String())}, nil + } + return result[0], nil +} + func (c *customResourceDefinition) IsBindable() (bool, error) { + + value, err := getValuesByJSONPath(c.resource.Object, "{..schema.openAPIV3Schema.properties.status.properties.binding.properties.name.type}") + if err == nil && len(value) > 0 && value[0].Interface().(string) == "string" { + return true, nil + } + annotations := make(map[string]string) util.MergeMaps(annotations, c.resource.GetAnnotations()) if len(annotations) == 0 { return false, nil } - val, found := annotations[binding.ProvisionedServiceAnnotationKey] - if found && val == "true" { - return true, nil - } - for k := range annotations { if ok, err := binding.IsServiceBindingAnnotation(k); ok && err == nil { return true, nil diff --git a/test/acceptance/resources/backend_crd.yaml b/test/acceptance/resources/backend_crd.yaml index 76aedaf771..0597091977 100644 --- a/test/acceptance/resources/backend_crd.yaml +++ b/test/acceptance/resources/backend_crd.yaml @@ -61,8 +61,11 @@ spec: properties: dbCredentials: type: string - bindings: - type: string + binding: + type: object + properties: + name: + type: string data: type: object properties: