Exposing binding data from a service

Application developers need access to backing services to build and connect workloads. Connecting workloads to backing services is always a challenge because each service provider requires a different way to access their secrets and consume them in a workload.

The Service Binding Operator enables application developers to easily bind workloads together with operator-managed backing services, without any manual procedures to configure the binding connection. For the Service Binding Operator to provide the binding data, as an Operator provider or user who creates backing services, you must expose the binding data to be automatically detected by the Service Binding Operator. Then, the Service Binding Operator automatically collects the binding data from the backing service and shares it with a workload to provide a consistent and predictable experience.

Methods of exposing binding data

This section describes the methods you can use to expose the binding data.

Ensure that you know and understand your workload requirements and environment, and how it works with the provided services.

Binding data is exposed under the following circumstances:

  • Backing service is available as a provisioned service resource.

    The service you intend to connect to is compliant with the Service Binding specification. You must create a Secret resource with all the required binding data values and reference it in the backing service custom resource (CR). The detection of all the binding data values is automatic.

  • Backing service is not available as a provisioned service resource.

    You must expose the binding data from the backing service. Depending on your workload requirements and environment, you can choose any of the following methods to expose the binding data:

    • Direct secret reference

    • Declaring binding data through custom resource definition (CRD) or CR annotations

    • Declaring binding data through Operator Lifecycle Manager (OLM) descriptors

    • Detection of binding data through owned resources

Provisioned service

Provisioned service represents a backing service CR with a reference to a Secret resource placed in the .status.binding.name field of the backing service CR.

As an Operator provider or the user who creates backing services, you can use this method to be compliant with the Service Binding specification, by creating a Secret resource and referencing it in the .status.binding.name section of the backing service CR. This Secret resource must provide all the binding data values required for a workload to connect to the backing service.

The following examples show an AccountService CR that represents a backing service and a Secret resource referenced from the CR.

Example: AccountService CR

  1. apiVersion: example.com/v1alpha1
  2. kind: AccountService
  3. name: prod-account-service
  4. spec:
  5. ...
  6. status:
  7. binding:
  8. name: hippo-pguser-hippo

Example: Referenced Secret resource

  1. apiVersion: v1
  2. kind: Secret
  3. metadata:
  4. name: hippo-pguser-hippo
  5. data:
  6. password: "MTBz"
  7. user: "Z3Vlc3Q="
  8. ...

When creating a service binding resource, you can directly give the details of the AccountService resource in the ServiceBinding specification as follows:

Example: ServiceBinding resource

  1. apiVersion: binding.operators.coreos.com/v1alpha1
  2. kind: ServiceBinding
  3. metadata:
  4. name: account-service
  5. spec:
  6. ...
  7. services:
  8. - group: "example.com"
  9. version: v1alpha1
  10. kind: AccountService
  11. name: prod-account-service
  12. application:
  13. name: spring-petclinic
  14. group: apps
  15. version: v1
  16. resource: deployments

Service Binding (Spec API Tech Preview) with the servicebinding.io API group is a Technology Preview feature only. Technology Preview features are not supported with Red Hat production service level agreements (SLAs) and might not be functionally complete. Red Hat does not recommend using them in production. These features provide early access to upcoming product features, enabling customers to test functionality and provide feedback during the development process. For more information about the support scope of Red Hat Technology Preview features, see https://access.redhat.com/support/offerings/techpreview/.

Example: ServiceBinding resource in Specification API

  1. apiVersion: servicebinding.io/v1alpha3
  2. kind: ServiceBinding
  3. metadata:
  4. name: account-service
  5. spec:
  6. ...
  7. service:
  8. apiVersion: example.com/v1alpha1
  9. kind: AccountService
  10. name: prod-account-service
  11. workload:
  12. apiVersion: apps/v1
  13. kind: Deployment
  14. name: spring-petclinic

This method exposes all the keys in the hippo-pguser-hippo referenced Secret resource as binding data that is to be projected into the workload.

Direct secret reference

You can use this method, if all the required binding data values are available in a Secret resource that you can reference in your Service Binding definition. In this method, a ServiceBinding resource directly references a Secret resource to connect to a service. All the keys in the Secret resource are exposed as binding data.

Example: Specification with the binding.operators.coreos.com API

  1. apiVersion: binding.operators.coreos.com/v1alpha1
  2. kind: ServiceBinding
  3. metadata:
  4. name: account-service
  5. spec:
  6. ...
  7. services:
  8. - group: ""
  9. version: v1
  10. kind: Secret
  11. name: hippo-pguser-hippo

Example: Specification that is compliant with the servicebinding.io API

  1. apiVersion: servicebinding.io/v1alpha3
  2. kind: ServiceBinding
  3. metadata:
  4. name: account-service
  5. spec:
  6. ...
  7. service:
  8. apiVersion: v1
  9. kind: Secret
  10. name: hippo-pguser-hippo

Declaring binding data through CRD or CR annotations

You can use this method to annotate the resources of the backing service to expose the binding data with specific annotations. Adding annotations under the metadata section alters the CRs and CRDs of the backing services. Service Binding Operator detects the annotations added to the CRs and CRDs and then creates a Secret resource with the values extracted based on the annotations.

The following examples show the annotations that are added under the metadata section and a referenced ConfigMap object from a resource:

Example: Exposing binding data from a Secret object defined in the CR annotations

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. service.binding: 'path={.metadata.name}-pguser-{.metadata.name},objectType=Secret'
  8. ...

The previous example places the name of the secret name in the {.metadata.name}-pguser-{.metadata.name} template that resolves to hippo-pguser-hippo. The template can contain multiple JSONPath expressions.

Example: Referenced Secret object from a resource

  1. apiVersion: v1
  2. kind: Secret
  3. metadata:
  4. name: hippo-pguser-hippo
  5. data:
  6. password: "MTBz"
  7. user: "Z3Vlc3Q="

Example: Exposing binding data from a ConfigMap object defined in the CR annotations

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. service.binding: 'path={.metadata.name}-config,objectType=ConfigMap'
  8. ...

The previous example places the name of the config map in the {.metadata.name}-config template that resolves to hippo-config. The template can contain multiple JSONPath expressions.

Example: Referenced ConfigMap object from a resource

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: hippo-config
  5. data:
  6. db_timeout: "10s"
  7. user: "hippo"

Declaring binding data through OLM descriptors

You can use this method if your backing service is provided by an Operator. If your Operator is distributed as an OLM bundle, you can add OLM descriptors to describe the binding data that is to be exposed. The OLM descriptors are part of Cluster Service Version resources. The Service Binding Operator detects the OLM descriptors and then creates a Secret resource with the values extracted based on the detected OLM descriptors.

You can expose the binding data by using the specDescriptors array and statusDescriptors array. The specDescriptors array specifies a path under the .spec section of a CR. The statusDescriptors array specifies a path under the .status section of a CR.

Following are the only two fields that are used for binding the data:

  • Path: A dot-delimited path of the field on the object as described by the descriptor.

  • X-Descriptors: Defines the binding data.

The following examples show how to define an X-Descriptor depending on the resource to which you point the path:

Example: X-Descriptor definition for exposing a secret

  1. - path: data.dbConfiguration
  2. x-descriptors:
  3. - urn:alm:descriptor:io.kubernetes:Secret
  4. - service.binding

Example: X-Descriptor definition for exposing a config map

  1. - path: data.dbConfiguration
  2. x-descriptors:
  3. - urn:alm:descriptor:io.kubernetes:ConfigMap
  4. - service.binding
  • You must have a service.binding entry in the X-Descriptors to identify that it is a configuration for service binding.

  • The absence of the Secret or ConfigMap specific X-Descriptors indicates that the descriptor is referencing the binding data value at the given path.

Detection of binding data through owned resources

You can use this method if your backing service owns one or more Kubernetes resources such as route, service, config map, or secret that you can use to detect the binding data. In this method, the Service Binding Operator detects the binding data from resources owned by the backing service CR.

The following examples show the detectBindingResources API option set to true in the ServiceBinding CR:

Example

  1. apiVersion: binding.operators.coreos.com/v1alpha1
  2. kind: ServiceBinding
  3. metadata:
  4. name: spring-petclinic-detect-all
  5. namespace: my-petclinic
  6. spec:
  7. detectBindingResources: true
  8. services:
  9. - group: postgres-operator.crunchydata.com
  10. version: v1beta1
  11. kind: PostgresCluster
  12. name: hippo
  13. application:
  14. name: spring-petclinic
  15. group: apps
  16. version: v1
  17. resource: deployments

In the previous example, PostgresCluster custom service resource owns one or more Kubernetes resources such as route, service, config map, or secret.

The Service Binding Operator automatically detects the binding data exposed on each of the owned resources.

Data model

The data model used in the annotations and OLM descriptors follow specific conventions.

Service binding annotations must use the following convention:

  1. service.binding(/<NAME>)?:
  2. "<VALUE>|(path=<JSONPATH_TEMPLATE>(,objectType=<OBJECT_TYPE>)?(,elementType=<ELEMENT_TYPE>)?(,sourceKey=<SOURCE_KEY>)?(,sourceValue=<SOURCE_VALUE>)?)"

where:

<NAME>

Specifies the name under which the binding value is to be exposed. You can exclude it only when the objectType parameter is set to Secret or ConfigMap.

<VALUE>

Specifies the constant value exposed when no path is set.

Although, the data model is the same for custom resource definitions (CRD), custom resource (CR) annotations, and Operator Lifecycle Manager (OLM) descriptors, the syntax for each one differs.

The data model provides the details on the allowed values and semantic for the path, elementType, objectType, sourceKey, and sourceValue parameters.

Table 1. Parameters and their descriptions
ParameterDescriptionDefault value

path

JSONPath template that consists JSONPath expressions enclosed by curly braces {}.

N/A

elementType

Specifies whether the value of the element referenced in the path parameter complies with any one of the following types:

  • string

  • sliceOfStrings

  • sliceOfMaps

string

objectType

Specifies whether the value of the element indicated in the path parameter refers to a ConfigMap, Secret, or plain string in the current namespace.

Secret, if elementType is non-string.

sourceKey

Specifies the key in the ConfigMap or Secret resource to be added to the binding secret when collecting the binding data.

Note:

  • When used in conjunction with elementType=sliceOfMaps, the sourceKey parameter specifies the key in the slice of maps whose value is used as a key in the binding secret.

  • Use this optional parameter to expose a specific entry in the referenced Secret or ConfigMap resource as binding data.

  • When not specified, all keys and values from the Secret or ConfigMap resource are exposed and are added to the binding secret.

N/A

sourceValue

Specifies the key in the slice of maps.

Note:

  • The value of this key is used as the base to generate the value of the entry for the key-value pair to be added to the binding secret.

  • In addition, the value of the sourceKey is used as the key of the entry for the key-value pair to be added to the binding secret.

  • It is mandatory only if elementType=sliceOfMaps.

N/A

The sourceKey and sourceValue parameters are applicable only if the element indicated in the path parameter refers to a ConfigMap or Secret resource.

RBAC requirements

To expose the backing service binding data using the Service Binding Operator, you require certain Role-based access control (RBAC) permissions. Specify certain verbs under the rules field of the ClusterRole resource to grant the RBAC permissions for the backing service resources. When you define these rules, you allow the Service Binding Operator to read the binding data of the backing service resources throughout the cluster. If the users do not have permissions to read binding data or modify application resource, the Service Binding Operator prevents such users to bind services to application. Adhering to the RBAC requirements avoids unnecessary permission elevation for the user and prevents access to unauthorized services or applications.

The Service Binding Operator performs requests against the Kubernetes API using a dedicated service account. By default, this account has permissions to bind services to workloads, both represented by the following standard Kubernetes or OpenShift objects:

  • Deployments

  • DaemonSets

  • ReplicaSets

  • StatefulSets

  • DeploymentConfigs

The Operator service account is bound to an aggregated cluster role, allowing Operator providers or cluster administrators to enable binding custom service resources to workloads. To grant the required permissions within a ClusterRole, label it with the servicebinding.io/controller flag and set the flag value to true. The following example shows how to allow the Service Binding Operator to get, watch, and list the custom resources (CRs) of Crunchy PostgreSQL Operator:

Example: Enable binding to PostgreSQL database instances provisioned by Crunchy PostgreSQL Operator

  1. apiVersion: rbac.authorization.k8s.io/v1
  2. kind: ClusterRole
  3. metadata:
  4. name: postgrescluster-reader
  5. labels:
  6. servicebinding.io/controller: "true"
  7. rules:
  8. - apiGroups:
  9. - postgres-operator.crunchydata.com
  10. resources:
  11. - postgresclusters
  12. verbs:
  13. - get
  14. - watch
  15. - list
  16. ...

This cluster role can be deployed during the installation of the backing service Operator.

Categories of exposable binding data

The Service Binding Operator enables you to expose the binding data values from the backing service resources and custom resource definitions (CRDs).

This section provides examples to show how you can use the various categories of exposable binding data. You must modify these examples to suit your work environment and requirements.

Exposing a string from a resource

The following example shows how to expose the string from the metadata.name field of the PostgresCluster custom resource (CR) as a username:

Example

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. service.binding/username: path={.metadata.name}
  8. ...

Exposing a constant value as the binding item

The following examples show how to expose a constant value from the PostgresCluster custom resource (CR):

Example: Exposing a constant value

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. "service.binding/type": "postgresql" (1)
1Binding type to be exposed with the postgresql value.

Exposing an entire config map or secret that is referenced from a resource

The following examples show how to expose an entire secret through annotations:

Example: Exposing an entire secret through annotations

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. service.binding: 'path={.metadata.name}-pguser-{.metadata.name},objectType=Secret'

Example: The referenced secret from the backing service resource

  1. apiVersion: v1
  2. kind: Secret
  3. metadata:
  4. name: hippo-pguser-hippo
  5. data:
  6. password: "MTBz"
  7. user: "Z3Vlc3Q="

The following example shows how to expose an entire config map through OLM descriptors:

Example: Exposing an entire config map through OLM descriptors

  1. - path: data.dbConfiguration
  2. x-descriptors:
  3. - urn:alm:descriptor:io.kubernetes:ConfigMap
  4. - service.binding

This example uses the path attribute with a urn:alm:descriptor:io.kubernetes:ConfigMap entry to indicate that the path points to the ConfigMap service resource.

If you intend to project all the values from a ConfigMap service resource, you must specify it as an attribute in the backing service CR. For example, if the attribute is part of the .spec section, you can create and use a specDescriptors array. Or, if the attribute is part of the .status section, you can create and use a statusDescriptors array.

Exposing a specific entry from a config map or secret that is referenced from a resource

The following examples show how to expose a specific entry from a config map through annotations:

Example: Exposing an entry from a config map through annotations

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. service.binding: 'path={.metadata.name}-config,objectType=ConfigMap,sourceKey=user'

Example: The referenced config map from the backing service resource

The binding data should have a key with name as db_timeout and value as 10s:

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: hippo-config
  5. data:
  6. db_timeout: "10s"
  7. user: "hippo"

The following example shows how to expose a specific entry from a config map through OLM descriptors:

Example: Exposing an entry from a config map through OLM descriptors

  1. - path: data.dbConfiguration
  2. x-descriptors:
  3. - urn:alm:descriptor:io.kubernetes:ConfigMap
  4. - service.binding:my_certificate:sourceKey=certificate

This example uses the path attribute with an X-Descriptors update for service.binding and sourceKey by providing the following information:

  • Name of the binding key that is to be projected

  • Name of the key in the Secret service resource

Exposing a resource definition value

The following example shows how to expose a resource definition value through annotations:

Example: Exposing a resource definition value through annotations

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. service.binding/username: path={.metadata.name}
  8. ...

The following example shows how to expose a resource definition value through OLM descriptors:

Example: Exposing a resource definition value through OLM descriptors

  1. - path: data.connectionURL
  2. x-descriptors:
  3. - service.binding:uri

The previous example uses the connectionURL attribute that points to the required resource definition value that is to be projected as uri.

If required values are available as attributes of backing service resources, annotating these values using X-Descriptors identifies them as the binding data.

Exposing entries of a collection with the key and value from each entry

The following example shows how to expose the entries of a collection with the key and value from each entry through annotations:

Example: Exposing the entries of a collection through annotations

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. "service.binding/uri": "path={.status.connections},elementType=sliceOfMaps,sourceKey=type,sourceValue=url"
  8. spec:
  9. ...
  10. status:
  11. connections:
  12. - type: primary
  13. url: primary.example.com
  14. - type: secondary
  15. url: secondary.example.com
  16. - type: '404'
  17. url: black-hole.example.com

The following example shows how the previous entries of a collection in annotations are projected into the bound application.

Example: Binding data files

  1. /bindings/<binding-name>/uri_primary => primary.example.com
  2. /bindings/<binding-name>/uri_secondary => secondary.example.com
  3. /bindings/<binding-name>/uri_404 => black-hole.example.com

The following example shows how to expose the entries of a collection with the key and value from each entry through OLM descriptors:

Example: Exposing the entries of a collection through OLM descriptors

  1. - path: bootstrap
  2. x-descriptors:
  3. - service.binding:endpoints:elementType=sliceOfMaps:sourceKey=type:sourceValue=url

The previous example uses the path attribute with an X-Descriptors update for the required entries of a collection.

Example: Configuration from a backing service resource

  1. status:
  2. connections:
  3. - type: primary
  4. url: primary.example.com
  5. - type: secondary
  6. url: secondary.example.com
  7. - type: '404'
  8. url: black-hole.example.com

The previous example helps you to project all those values with keys such as primary, secondary, and so on.

Exposing items of a collection with one key per item

The following example shows how to expose the items of a collection with one key per item through annotations:

Example: Exposing the items of a collection through annotations

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. "service.binding/tags": "path={.spec.tags},elementType=sliceOfStrings"
  8. spec:
  9. tags:
  10. - knowledge
  11. - is
  12. - power

The following example shows how the previous items of a collection in annotations are projected into the bound application.

Example: Binding data files

  1. /bindings/<binding-name>/tags_0 => knowledge
  2. /bindings/<binding-name>/tags_1 => is
  3. /bindings/<binding-name>/tags_2 => power

The following example shows how to expose the items of a collection with one key per item through OLM descriptors:

Example: Exposing the items of a collection through OLM descriptors

  1. - path: spec.tags
  2. x-descriptors:
  3. - service.binding:tags:elementType=sliceOfStrings

The previous example uses the path attribute with an X-Descriptors update for the required items of a collection.

Example: Configuration from a backing service resource

  1. spec:
  2. tags:
  3. - knowledge
  4. - is
  5. - power

Exposing values of collection entries with one key per entry value

The following example shows how to expose the values of collection entries with one key per entry value through annotations:

Example: Exposing the values of collection entries through annotations

  1. apiVersion: postgres-operator.crunchydata.com/v1beta1
  2. kind: PostgresCluster
  3. metadata:
  4. name: hippo
  5. namespace: my-petclinic
  6. annotations:
  7. "service.binding/url": "path={.spec.connections},elementType=sliceOfStrings,sourceValue=url"
  8. spec:
  9. connections:
  10. - type: primary
  11. url: primary.example.com
  12. - type: secondary
  13. url: secondary.example.com
  14. - type: '404'
  15. url: black-hole.example.com

The following example shows how the previous values of a collection in annotations are projected into the bound application.

Example: Binding data files

  1. /bindings/<binding-name>/url_0 => primary.example.com
  2. /bindings/<binding-name>/url_1 => secondary.example.com
  3. /bindings/<binding-name>/url_2 => black-hole.example.com

The following example shows how to expose the values of collection entries with one key per entry value through OLM descriptors:

Example: Exposing the values of collection entries through OLM descriptors

  1. - path: bootstrap
  2. x-descriptors:
  3. - service.binding:endpoints:elementType=sliceOfStrings:sourceValue=url

Additional resources