Skip to content

Create Mappings Extension #145

@nebhale

Description

@nebhale

Currently, the ServiceBinding type contains a mappings element that allows the creation of mappings from the contents of a ProvisionedService’s Secret to a new Secret projected into an Application. The inclusion of this element, while reducing the number of resources that a user might need to create, had some significant downsides.

  • The ServiceBinding reconciler needed to have permissions to read existing Secrets.
  • A single fixed set of mapping syntaxes had to be defined
  • If a platform wanted additional or alternate mapping syntaxes they had to create their own ServiceBinding reconciler and ensure that only a single implementation was installed.

Proposal

The mapping element should be removed from the ServiceBinding resource and its functionality should be moved into a set of discrete resource types devoted to this mapping. For example, instead of defining

apiVersion: service.binding/v1alpha2
kind: ServiceBinding
metadata:
  name: account-service
spec:
  application:
    apiVersion: apps/v1
    kind:       Deployment
    name:       online-banking
  service:
    apiVersion: com.example/v1alpha1
    kind:       AccountService
    name:       prod-account-service
  mappings:
  - name:  accountServiceUri
    value: https://{{ urlquery .username }}:{{ urlquery .password }}@{{ .host }}:{{ .port }}/{{ .path }}

a user would define multiple resource types

apiVersion: service.binding/v1alpha2
kind: ServiceBinding
metadata:
  name: account-service
spec:
  application:
    apiVersion: apps/v1
    kind:       Deployment
    name:       online-banking
  service:
    apiVersion: mapping.service.binding/v1alpha2
    kind:       GoV1Template
    name:       mapped-prod-account-service

---
apiVersion: mapping.service.binding/v1alpha2
kind: GoV1Template
metadata:
  name: mapped-prod-account-service
spec:
  service:
    apiVersion: com.example/v1alpha1
    kind:       AccountService
    name:       prod-account-service
  mappings:
  - name:  accountServiceUri
    value: https://{{ urlquery .username }}:{{ urlquery .password }}@{{ .host }}:{{ .port }}/{{ .path }}

Benefits

This change would immediately improve the permissions situation removing the need for the ServiceBinding reconciler to have permissions to read Secrets. Instead a user could elect whether to add mapping behavior or not based on whether they felt the value/risk of having a third-party controller with access to Secrets was worth it.

If the user wanted mappings, but didn’t trust our implementation this change also provides a point of extensibility allowing a user to install another implementation that they trust more or even implement a mapping reconciler themselves.

That extensibility isn’t just beneficial to users concerned with security either. This same point of extensibility also allows the introduction of an arbitrary number of mapping syntaxes. While we might have implementations for OLM, Go V1 Templates, and more, the project wouldn’t be gatekeepers for platform-specific or user-specific templating syntaxes. Each user could implement or install a mapping controller that suited their needs.

A design that moves mappings out so that they are just another implementation of the ProvisionedService duck type also results in architectural benefits. It improves the separation of concerns (ServiceBinding only cares about performing the binding, not also doing an inline mapping), it simplifies the mapping of the most critical controller, and it enables composability of mappings.

apiVersion: service.binding/v1alpha2
kind: ServiceBinding
metadata:
  name: account-service
spec:
  application:
    apiVersion: apps/v1
    kind:       Deployment
    name:       online-banking
  service:
    apiVersion: mapping.service.binding/v1alpha2
    kind:       TypeProvider
    name:       typed-prod-account-service

---
apiVersion: mapping.service.binding/v1alpha2
kind: TypeProvider
metadata:
  name: typed-prod-account-service
spec:
  service:
    apiVersion: mapping.service.binding/v1alpha2
    kind:       GoV1Template
    name:       mapped-prod-account-service
  type: my-account-service-type
  provider: my-company

---
apiVersion: mapping.service.binding/v1alpha2
kind: GoV1Template
metadata:
  name: mapped-prod-account-service
spec:
  service:
    apiVersion: mapping.service.binding/v1alpha2
    kind:       JSONPath
    name:       extracted-prod-account-service
  mappings:
  - name:  accountServiceUri
    template: https://{{ urlquery .username }}:{{ urlquery .password }}@{{ .host }}:{{ .port }}/{{ .path }}

---
apiVersion: mapping.service.binding/v1alpha2
kind: JSONPath
metadata:
  name: extracted-prod-account-service
spec:
  service:
    apiVersion: com.example/v1alpha1
    kind:       AccountService
    name:       prod-account-service
  mappings:
  - name:  username
    path: .status.credentials.username
  - name:  password
    path: .status.credentials.password
  - name:  host
    path: .status.endpoint.host
  - name:  port
    path: .status.endpoint.port
  - name:  path
    path: .status.endpoint.path

Finally by moving this feature, for which each platform has diverging needs, out of the reference implementation it reduces the need to have multiple implementations of the specification which reduces the possibility of collisions within a given cluster.

Outcomes

There are a couple of options for what happens to the mapping functionality after it is extracted.

First, it could become a purely community concern. Each platform vendor would be free to implement their own controllers for their own mapping styles. This option provides ultimate flexibility at the expense of interoperability.

Second, a set of mapping types could be included in the project implementation, but not as part of the specification. This would not preclude vendors from adding their own mapping styles in their platforms. This option maintains much of the flexibility of the previous option while adding some level of interoperability for commonly used mapping needs.

Third, a set of mappings could be included in the specification as well as in the implementation of the specification. This would not preclude vendors from adding their own mapping styles in their platforms. This option creates the highest level of interoperability at the expense of flexibility.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions