Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove mappings from the ServiceBinding resource #154

Merged
merged 3 commits into from
Jun 14, 2021

Conversation

scothis
Copy link
Contributor

@scothis scothis commented May 14, 2021

The existing mappings on the ServiceBinding resource were introduced as
a way to make it easier for a user to enrich an existing Secret into a
form appropriate for binding to an application workload. This approch
had a few issues that can be better addressed by other resources that
interoperate with the ServiceBiding resource. The issues include:

  • the ServiceBinding controller needs to be able to read and write
    Secrets
  • Go templates were used to compose new values, which worked for basic
    templating, but were limited in their capabilities
  • the capabilities of the Go templates are an implemenation detail of
    how the controller is built and could change over time independent of
    the speced behavior.

The behavior applied by mappings can be reintroduced as dedicated
resources that can themselves expose a Secret as a ProvisionedService,
which can be consumed by a ServiceBinding. This change further separates
the concerns of provisioning a service from binding a service.

The .spec.type and .spec.provider fields are also removed, as
they are syntactic sugar on top of mappings.

Refs #145
Resolves #155

Signed-off-by: Scott Andrews [email protected]

The existing mappings on the ServiceBinding resource were introduced as
a way to make it easier for a user to enrich an existing Secret into a
form appropriate for binding to an application workload. This approch
had a few issues that can be better addressed by other resources that
interoperate with the ServiceBiding resource. The issues include:
- the ServiceBinding controller needs to be able to read and write
  Secrets
- Go templates were used to compose new values, which worked for basic
  templating, but were limited in their capabilities
- the capabilities of the Go templates are an implemenation detail of
  how the controller is built and could change over time independent of
  the speced behavior.

The behavior applied by mappings can be reintroduced as dedicated
resources that can themselves expose a Secret as a ProvisionedService,
which can be consumed by a ServiceBinding. This change further separates
the concerns of provisioning a service from binding a service.

Refs servicebinding#145

Signed-off-by: Scott Andrews <[email protected]>
scothis added a commit to scothis/service-bindings that referenced this pull request May 14, 2021
The functionality enabled by mappings can/will be replaced with
dedicated resources that can decorate a secret exposing the
ProvisionedService duck-type which can be consumed by a ServiceBinding.

Refs servicebinding/spec#154

Signed-off-by: Scott Andrews <[email protected]>
scothis added a commit to scothis/service-bindings that referenced this pull request May 14, 2021
The functionality enabled by mappings can/will be replaced with
dedicated resources that can decorate a secret exposing the
ProvisionedService duck-type which can be consumed by a ServiceBinding.

Refs servicebinding/spec#154

Signed-off-by: Scott Andrews <[email protected]>
Copy link
Contributor

@baijum baijum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nebhale nebhale requested review from nebhale and arthurdm May 19, 2021 15:17
@scothis
Copy link
Contributor Author

scothis commented May 25, 2021

Added a clarification to resolve #155

@@ -115,6 +114,8 @@ An implementation is not compliant if it fails to satisfy one or more of the MUS

A Provisioned Service resource **MUST** define a `.status.binding` which is a `LocalObjectReference`-able (containing a single field `name`) to a `Secret`. The `Secret` **MUST** be in the same namespace as the resource. The `Secret` data **SHOULD** contain a `type` entry with a value that identifies the abstract classification of the binding. The `Secret` type (`.type` verses `.data.type`) **SHOULD** reflect this value as `service.binding/{type}`, replacing `{type}` with the `Secret` data type. It is **RECOMMENDED** that the `Secret` data also contain a `provider` entry with a value that identifies the provider of the binding. The `Secret` data **MAY** contain any other entry. To facilitate discoverability, it is **RECOMMENDED** that a `CustomResourceDefinition` exposing a Provisioned Service add `service.binding/provisioned-service: "true"` as a label.

> Note: While the Provisioned Service referenced `Secret` data should contain a `type` entry, the `type` must be defined before it is projected into an application workload. This allows a mapping to enrich an existing secret.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a mapping to enrich

@scothis
Are you referring to the "mappings" as described in #145?
Alternatively, the .spec.type (optional) value available in the ServiceBinding resource can be used in the binding Secret, right? Should we add that info into the note here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.spec.type is removed by this PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed that now :-)

The removal of .spec.type and .spec.provider should be called out in the PR description because I don't see mentioning it in #145.

BTW, I am fine with removing .spec.type and .spec.provider. That way, the reconciler can mount the Secret resource into the application workload, and no need to create an intermediate Secret resource.

I hope soon the extensions mentioned in #145 will become a reality!

Copy link
Contributor Author

@scothis scothis May 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of .spec.type and .spec.provider should be called out in the PR description

updated the PR subject and overview

I hope soon the extensions mentioned in #145 will become a reality!

nothing to wait for. Start creating and using them today; they're just not part of the spec, yet(?)

@scothis scothis changed the title Remove mappings from the ServiceBinding resource Remove mappings, type and provider from the ServiceBinding resource May 25, 2021
@scothis
Copy link
Contributor Author

scothis commented May 27, 2021

Based on the call today, we want to reevaluate removing the .spec.type and .spec.provider fields. The thought is that we can create additional volumes/volumemounts for these specific files when needed. Before taking this approach, we need to ensure Kubernetes volume mounts can be safely nested, and what happens when there is a collision (e.g. the type exists, but is being overridden).

@baijum
Copy link
Contributor

baijum commented May 28, 2021

I tried the following configuration but seems to be not working. This is the error message in the pod:

OCI runtime create failed: container_linux.go:367: starting container process caused:
process_linux.go:495: container init caused: rootfs_linux.go:60:
mounting "/var/lib/kubelet/pods/4f575521-2efb-4a06-9ea0-1ba93dfe1927/volume-subpaths/type/busybox/1"
to rootfs at 
"/var/lib/docker/overlay2/30f1880fedd229c6bda44de1eda54038751dc49f2456ff6a15821732f6b9eab5/merged/bindings/type" 
caused: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)?
Check if the specified host path exists and is the expected type
---
apiVersion: v1
kind: Secret
metadata:
  name: secret1
type: Opaque
stringData:
  provider: "backingservice"
  username: "guest"
  password: "passwd"
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm1
data:
  type: custom
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  labels:
    environment: test
spec:
  selector:
    matchLabels:
      environment: test
  template:
    metadata:
      labels:
        environment: test
    spec:
      containers:
        - name: "busybox"
          image: "busybox:latest"
          command: ["sleep"]
          args: ["1h"]
          volumeMounts:
            - name: bindings
              mountPath: /bindings
            - name: type
              mountPath: /bindings/type
              subPath: type
      volumes:
        - name: bindings
          secret:
            secretName: secret1
        - name: type
          configMap:
            name: cm1
            items:
              - key: type
                path: type

@baijum
Copy link
Contributor

baijum commented May 28, 2021

I just read the docs:

Volumes can not mount onto other volumes or have hard links to other volumes.

@baijum
Copy link
Contributor

baijum commented May 28, 2021

I tried to use life cycle hooks after setting readOnly: false in the volumeMounts. But due to security reasons setting readOnly to false has no impact, it will be always readOnly: true.

...
          volumeMounts:
            - name: bindings
              mountPath: /bindings
              readOnly: false
            - name: type
              mountPath: /extra-bindings/type
              subPath: type
          lifecycle:
            postStart:
              exec:
                command:
                  - /bin/sh
                  - -c
                  - ln -sf /extra-bindings/type /bindings/type
...

This is the error message:

PostStartHookError: command '/bin/sh -c ln -sf /extra-bindings/type /bindings/type'
exited with 1: ln: /bindings/type: Read-only file system

@scothis
Copy link
Contributor Author

scothis commented May 28, 2021

@baijum thanks for the research

@baijum
Copy link
Contributor

baijum commented May 31, 2021

@scothis A projected volume maps several existing volume sources into the same directory.

Here is a modified example:

---
apiVersion: v1
kind: Secret
metadata:
  name: secret1
type: Opaque
stringData:
  type: "to-be-changed"
  provider: "backingservice"
  username: "guest"
  password: "passwd"
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm1
data:
  type: custom
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  labels:
    environment: test
spec:
  selector:
    matchLabels:
      environment: test
  template:
    metadata:
      labels:
        environment: test
    spec:
      containers:
        - name: "busybox"
          image: "busybox:latest"
          command: ["sleep"]
          args: ["1h"]
          volumeMounts:
            - name: all-in-one
              mountPath: /bindings
      volumes:
        - name: all-in-one
          projected:
            sources:
              - secret:
                  name: secret1
              - configMap:
                  name: cm1
                  items:
                    - key: type
                      path: type

As you can see the original value for type in the Secret resource is getting overridden by the value in the ConfigMap.
I confirmed it:

$ kubectl exec -it app-774f8fcd48-mp9g9 -- cat /bindings/type 
custom

I tried to change some values in the Secret resource, and that is getting reflected in the file system immediately.

So, we are good to go with merging this PR :-)

Edit: typo

@baijum
Copy link
Contributor

baijum commented May 31, 2021

we are good to go with merging this PR

Based on the above information, we can retain .spec.type and .spec.provider as optional fields. I think we can add an additional note about these special fields in the spec. Something like this:

Note: Users can employ the `.spec.type` and `.spec.provider` fields to override the original
values in the Provisioned Service Secret resource. Mainly, it would be helpful if the
Provisioned Service Secret resource doesn't provide those values.

(Feel free to rephrase)

@baijum
Copy link
Contributor

baijum commented Jun 1, 2021

The Application Projection section 2nd paragraph also needs some re-write. This is how it currently reads:

The Secret data MUST contain a type entry with a value that identifies the abstract classification of the binding. The Secret type (.type verses .data.type) MUST reflect this value as service.binding/{type}, replacing {type} with the Secret data type. It is RECOMMENDED that the Secret data also contain a provider entry with a value that identifies the provider of the binding. The Secret data MAY contain any other entry.

While bring back these fields and the general capability, I removed
references that mandated these fields be added to a Secret. Instead, the
requirement is that they are part of the application projection.
Implementors can figure out the best way to make that happen, either by
creating a derivative Secret, or by using a projected volume.

Signed-off-by: Scott Andrews <[email protected]>
@scothis
Copy link
Contributor Author

scothis commented Jun 3, 2021

@baijum updated to restore the type and provider fields on the ServiceBindingSpec. Removed references to "secrets" in the application projection as a Secret is more of an implementation detail at this point.

Copy link
Contributor

@baijum baijum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@baijum
Copy link
Contributor

baijum commented Jun 10, 2021

Remove mappings, type and provider from the ServiceBinding resource

@scothis Please update the title of this PR: type and provider is not removed.

@scothis scothis changed the title Remove mappings, type and provider from the ServiceBinding resource Remove mappings from the ServiceBinding resource Jun 10, 2021
@@ -115,6 +114,8 @@ An implementation is not compliant if it fails to satisfy one or more of the MUS

A Provisioned Service resource **MUST** define a `.status.binding` which is a `LocalObjectReference`-able (containing a single field `name`) to a `Secret`. The `Secret` **MUST** be in the same namespace as the resource. The `Secret` data **SHOULD** contain a `type` entry with a value that identifies the abstract classification of the binding. The `Secret` type (`.type` verses `.data.type`) **SHOULD** reflect this value as `service.binding/{type}`, replacing `{type}` with the `Secret` data type. It is **RECOMMENDED** that the `Secret` data also contain a `provider` entry with a value that identifies the provider of the binding. The `Secret` data **MAY** contain any other entry. To facilitate discoverability, it is **RECOMMENDED** that a `CustomResourceDefinition` exposing a Provisioned Service add `service.binding/provisioned-service: "true"` as a label.

> Note: While the Provisioned Service referenced `Secret` data should contain a `type` entry, the `type` must be defined before it is projected into an application workload. This allows a mapping to enrich an existing secret.
Copy link
Member

@arthurdm arthurdm Jun 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed some key words (should / must) weren't in the proper format, and I think the wording was a bit confusing in terms of what the spec is saying must be present (which is the type in the projection, not the secret)

Suggested change
> Note: While the Provisioned Service referenced `Secret` data should contain a `type` entry, the `type` must be defined before it is projected into an application workload. This allows a mapping to enrich an existing secret.
> Note: While the Provisioned Service referenced `Secret` data **SHOULD** contain a `type` entry, the `type` **MUST** be present in the application projection.

Copy link
Contributor Author

@scothis scothis Jun 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a non-normative block, so it intentionally uses unstylized "should" and "must". The spec requirements are captured in the Application Projection section.

The driver for this note is #155

@arthurdm arthurdm merged commit 2dceb46 into servicebinding:master Jun 14, 2021
@scothis scothis deleted the mappings branch June 14, 2021 13:59
scothis added a commit to scothis/service-bindings that referenced this pull request Jul 20, 2021
The functionality enabled by mappings can/will be replaced with
dedicated resources that can decorate a secret exposing the
ProvisionedService duck-type which can be consumed by a ServiceBinding.

Custom type and provider fields are now managed with projected volumes
and field path env vars.

Refs servicebinding/spec#154

Signed-off-by: Scott Andrews <[email protected]>
scothis added a commit to vmware-tanzu/servicebinding that referenced this pull request Jul 20, 2021
The functionality enabled by mappings can/will be replaced with
dedicated resources that can decorate a secret exposing the
ProvisionedService duck-type which can be consumed by a ServiceBinding.

Custom type and provider fields are now managed with projected volumes
and field path env vars.

Refs servicebinding/spec#154

Signed-off-by: Scott Andrews <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Inconsistent information about type entry in binding secret
4 participants