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

Korifi API users should be able to provide JSON objects as user provided service parameters #2900

Closed
georgethebeatle opened this issue Sep 26, 2023 · 3 comments · Fixed by #3140
Labels
bug Something isn't working

Comments

@georgethebeatle
Copy link
Member

georgethebeatle commented Sep 26, 2023

According to the service credential binding and service instance documentation, the parameters request parameter is A JSON object that is passed to the service broker. This means that users should be able to create a user provided service via the following command:

cf create-user-provided-service s2 -p '{"a":"b", "c":{"d": "e"}}'

On CF for VMs the command above succeeds:

❯ cf create-user-provided-service s2 -p '{"a":"b", "c":{"d": "e"}}' -v
Creating user provided service s2 in org org1 / space space1 as admin...
REQUEST: [2023-09-27T11:41:46Z]
POST /v3/service_instances HTTP/1.1
Host: api.mel-c.korifi.cf-app.com
{
  "credentials": {
    "a": "b",
    "c": {
      "d": "e"
    }
  },
  "name": "s2",
  "relationships": {
    "space": {
      "data": {
        "guid": "5e8590f4-b7db-4529-9f4e-6e4af97abd5a"
      }
    }
  },
  "type": "user-provided"
}


RESPONSE: [2023-09-27T11:41:46Z]
HTTP/1.1 201 Created
{
  "created_at": "2023-09-27T11:41:46Z",
  "guid": "aff1e51d-4a42-406e-abe9-bdb0025cdb7d",
  ....
}


OK

However, on Korifi we get the following error:

❯ cf create-user-provided-service s2 -p '{"a":"b", "c":{"d": "e"}}' -v
Creating user provided service s2 in org org1 / space space1 as cf-admin...
REQUEST: [2023-09-27T11:49:59Z]
POST /v3/service_instances HTTP/1.1
Host: localhost
{
  "credentials": {
    "a": "b",
    "c": {
      "d": "e"
    }
  },
  "name": "s2",
  "relationships": {
    "space": {
      "data": {
        "guid": "cf-space-17c91428-0a29-4a7c-83ba-3740c6276830"
      }
    }
  },
  "type": "user-provided"
}


RESPONSE: [2023-09-27T11:49:59Z]
HTTP/1.1 422 Unprocessable Entity
{
  "errors": [
    {
      "code": 10008,
      "detail": "Credentials must be a string",
      "title": "CF-UnprocessableEntity"
    }
  ]
}


Credentials must be a string
FAILED

The reason for this error is that we expect that the parameters field is map[string]string. In order to support parameters that are json object we should change that to map[string]any. We should ensure that we implement this in a backwards compatible manner, epscially with regards to existing user provided services and the secrets they use to store their parameters.

See this slack discussion for more context

@georgethebeatle georgethebeatle converted this from a draft issue Sep 26, 2023
@georgethebeatle georgethebeatle moved this from ⚙️ Chores to 🔄 In progress in Korifi - Backlog Sep 27, 2023
@danail-branekov danail-branekov changed the title Create issue for: https://cloudfoundry.slack.com/archives/C0297673ASK/p1695309854906089 Korifi API users should be able to provide JSON objects as user provided service parameters Sep 27, 2023
@danail-branekov danail-branekov moved this from 🔄 In progress to 🇪🇺 To do in Korifi - Backlog Sep 27, 2023
@georgethebeatle georgethebeatle moved this from 🇪🇺 To do to 🔄 In progress in Korifi - Backlog Jan 29, 2024
@danail-branekov
Copy link
Member

@danail-branekov
Copy link
Member

danail-branekov commented Feb 1, 2024

We got feedback from @vlast3k that some apps would fail when unmarshalling the parameters object if an extra property (such as .metadata) is available. We should therefore ensure we filter out extra fields when building the VCAP_SERVICES content.

In addition to that, Korifi adds a type key to the secret that is eventually getting exposed in VCAP_SERVICES. Maybe we should be filtering it as well.
Also, what would happen if the user provides a type parameters key? Is there going to be a collision?

@georgethebeatle georgethebeatle moved this from 🔄 In progress to 🇪🇺 To do in Korifi - Backlog Feb 1, 2024
@georgethebeatle georgethebeatle moved this from 🇪🇺 To do to 🔄 In progress in Korifi - Backlog Feb 6, 2024
@georgethebeatle
Copy link
Member Author

georgethebeatle commented Feb 8, 2024

We had an alternative spike on the issue, see this commit

  • Accept map[string]any instead of map[string]string credentials
    when creating a user-provided service instance

  • Backwards compatibility with existing UPSIs and bindings is achieved
    by

    • The CFServiceInstance controller is migrating existing credentials
      secret to a new one that has a single data key that contains the
      marshalled credentials object, the old (legacy) secret is preserved.
    • Introduce CFServiceInstanceStatus.Credentials field to reference
      the service instance secret. For legacy service instances this
      references the migrated secret, while newly created service
      instances reference the newly created secret in the format above.
    • The VCAP_SERVICES builder uses the credentials secret in the
      service instance status (that is guaranteed to be in the new format)
      to build the content of the VCAP_SERVICES env var.
    • Newly created bindings for existing service instances would make use
      of the migrated secret
    • Existing bindings have their CFServiceBindingStatus.Binding
      ducktype set to the old legacy service instance credentials secret.
      The CFServiceBinding controller would notice this and would not
      change them. Thus old bindings would still work, and most
      importantly, would not change and would not cause workloads
      statefulset restart.
  • For newly created bindings, the CFServiceBinding controller
    reconciles the service instance secret (the one with the single
    data key) and creates a new secret with multiple keys (that is the
    format of the legacy secret). This secret is used on the
    servicebinding.io ServiceBinding to ensure the expected
    map[string]string format on the filesystem mount

  • We have removed the CFServiceInstanceStatus.Binding ducktype as it
    turned out to be unused.

Pros:

  • Completely backwards compatible
  • No statefulset restart on Korifi update
  • VCAP_SERVICES credentials attributes are populated precisely the way the user provided them in the cf create-user-provider-service command, there are no additional typeand provider fields. This addresses picky applications that expect credentials to contain very specific keys.
  • The migration logic could be removed once we decide that all users have rebound their apps

Cons:

  • None, besides being a bit complex

danail-branekov added a commit that referenced this issue Feb 23, 2024
Previously Korifi supported plain `map[string]string` for UPSI
credentials. This deviates from the CF API spec where credentials can be
arbitrary objects.

As credentials are stored in secrets which are glorified
`map[string][]byte` we had to change the way we store the credentials
object:
* We marshal the credentials object into a byte array and store it under
  the `crdentials` key in the secret. We no longer use a secret key per
  every credentials value. The `CFServiceInstance.Spec.SecretName`
  references that credentials secret.
* We introduce `CFServiceInstance.Status.Credentials` and
  `CFServiceInstance.Status.CredentialsObservedVersion` that aremanaged
  by the `CFServiceInstance` controller. Once the controller
  reconciles the service instance, it sets the secret name and its
  resource version into the new status fields.
* The CFServiceBindingController reconciles the CFServiceInstance
  credentials secret into the flat format that is then used for the
  servicebinding.io binding. In case of a json object value to a key,
  the value is represented as a json string.
* Existing bindings are not affected when upgrading Korifi. However,
  once the user updates the service instance credentials, the binding
  secret is rebuilt which causes statefulset restart. Further updates to
  the credentials will not cause additional restarts.

fixes #2900

Co-authored-by: Danail Branekov <[email protected]>
danail-branekov added a commit that referenced this issue Feb 23, 2024
Previously Korifi supported plain `map[string]string` for UPSI
credentials. This deviates from the CF API spec where credentials can be
arbitrary objects.

As credentials are stored in secrets which are glorified
`map[string][]byte` we had to change the way we store the credentials
object:
* We marshal the credentials object into a byte array and store it under
  the `crdentials` key in the secret. We no longer use a secret key per
  every credentials value. The `CFServiceInstance.Spec.SecretName`
  references that credentials secret.
* We introduce `CFServiceInstance.Status.Credentials` and
  `CFServiceInstance.Status.CredentialsObservedVersion` that aremanaged
  by the `CFServiceInstance` controller. Once the controller
  reconciles the service instance, it sets the secret name and its
  resource version into the new status fields.
* The CFServiceBindingController reconciles the CFServiceInstance
  credentials secret into the flat format that is then used for the
  servicebinding.io binding. In case of a json object value to a key,
  the value is represented as a json string.
* Existing bindings are not affected when upgrading Korifi. However,
  once the user updates the service instance credentials, the binding
  secret is rebuilt which causes statefulset restart. Further updates to
  the credentials will not cause additional restarts.

fixes #2900

Co-authored-by: Danail Branekov <[email protected]>
georgethebeatle added a commit that referenced this issue Feb 23, 2024
Previously Korifi supported plain `map[string]string` for UPSI
credentials. This deviates from the CF API spec where credentials can be
arbitrary objects.

As credentials are stored in secrets which are glorified
`map[string][]byte` we had to change the way we store the credentials
object:
* We marshal the credentials object into a byte array and store it under
  the `crdentials` key in the secret. We no longer use a secret key per
  every credentials value. The `CFServiceInstance.Spec.SecretName`
  references that credentials secret.
* We introduce `CFServiceInstance.Status.Credentials` and
  `CFServiceInstance.Status.CredentialsObservedVersion` that aremanaged
  by the `CFServiceInstance` controller. Once the controller
  reconciles the service instance, it sets the secret name and its
  resource version into the new status fields.
* The CFServiceBindingController reconciles the CFServiceInstance
  credentials secret into the flat format that is then used for the
  servicebinding.io binding. In case of a json object value to a key,
  the value is represented as a json string.
* Existing bindings are not affected when upgrading Korifi. However,
  once the user updates the service instance credentials, the binding
  secret is rebuilt which causes statefulset restart. Further updates to
  the credentials will not cause additional restarts.

fixes #2900

Co-authored-by: Danail Branekov <[email protected]>
danail-branekov added a commit that referenced this issue Feb 23, 2024
Previously Korifi supported plain `map[string]string` for UPSI
credentials. This deviates from the CF API spec where credentials can be
arbitrary objects.

As credentials are stored in secrets which are glorified
`map[string][]byte` we had to change the way we store the credentials
object:
* We marshal the credentials object into a byte array and store it under
  the `crdentials` key in the secret. We no longer use a secret key per
  every credentials value. The `CFServiceInstance.Spec.SecretName`
  references that credentials secret.
* We introduce `CFServiceInstance.Status.Credentials` and
  `CFServiceInstance.Status.CredentialsObservedVersion` that aremanaged
  by the `CFServiceInstance` controller. Once the controller
  reconciles the service instance, it sets the secret name and its
  resource version into the new status fields.
* The CFServiceBindingController reconciles the CFServiceInstance
  credentials secret into the flat format that is then used for the
  servicebinding.io binding. In case of a json object value to a key,
  the value is represented as a json string.
* Existing bindings are not affected when upgrading Korifi. However,
  once the user updates the service instance credentials, the binding
  secret is rebuilt which causes statefulset restart. Further updates to
  the credentials will not cause additional restarts.
* We have created ADR 16 documenting how this works

fixes #2900

Co-authored-by: Danail Branekov <[email protected]>
danail-branekov added a commit that referenced this issue Mar 11, 2024
Previously Korifi supported plain `map[string]string` for UPSI
credentials. This deviates from the CF API spec where credentials can be
arbitrary objects.

As credentials are stored in secrets which are glorified
`map[string][]byte` we had to change the way we store the credentials
object:
* We marshal the credentials object into a byte array and store it under
  the `crdentials` key in the secret. We no longer use a secret key per
  every credentials value. The `CFServiceInstance.Spec.SecretName`
  references that credentials secret.
* We introduce `CFServiceInstance.Status.Credentials` and
  `CFServiceInstance.Status.CredentialsObservedVersion` that aremanaged
  by the `CFServiceInstance` controller. Once the controller
  reconciles the service instance, it sets the secret name and its
  resource version into the new status fields.
* The CFServiceBindingController reconciles the CFServiceInstance
  credentials secret into the flat format that is then used for the
  servicebinding.io binding. In case of a json object value to a key,
  the value is represented as a json string.
* Existing bindings are not affected when upgrading Korifi. However,
  once the user updates the service instance credentials, the binding
  secret is rebuilt which causes statefulset restart. Further updates to
  the credentials will not cause additional restarts.
* We have created ADR 16 documenting how this works

fixes #2900

Co-authored-by: Danail Branekov <[email protected]>
@github-project-automation github-project-automation bot moved this from 🔄 In progress to ✅ Done in Korifi - Backlog Mar 11, 2024
danail-branekov added a commit that referenced this issue Mar 12, 2024
rather than map[string]string

Issue: #2900

Co-authored-by: Danail Branekov <[email protected]>
danail-branekov added a commit that referenced this issue Mar 12, 2024
rather than map[string]string

Issue: #2900

Co-authored-by: Danail Branekov <[email protected]>
georgethebeatle added a commit that referenced this issue Mar 13, 2024
rather than map[string]string

Issue: #2900

Co-authored-by: Danail Branekov <[email protected]>
Co-authored-by: Georgi Sabev <[email protected]>
georgethebeatle added a commit that referenced this issue Mar 13, 2024
…rvices

Credentials in VCAP_SERVICES are objects
marsteg pushed a commit to marsteg/korifi that referenced this issue Mar 19, 2024
Previously Korifi supported plain `map[string]string` for UPSI
credentials. This deviates from the CF API spec where credentials can be
arbitrary objects.

As credentials are stored in secrets which are glorified
`map[string][]byte` we had to change the way we store the credentials
object:
* We marshal the credentials object into a byte array and store it under
  the `crdentials` key in the secret. We no longer use a secret key per
  every credentials value. The `CFServiceInstance.Spec.SecretName`
  references that credentials secret.
* We introduce `CFServiceInstance.Status.Credentials` and
  `CFServiceInstance.Status.CredentialsObservedVersion` that aremanaged
  by the `CFServiceInstance` controller. Once the controller
  reconciles the service instance, it sets the secret name and its
  resource version into the new status fields.
* The CFServiceBindingController reconciles the CFServiceInstance
  credentials secret into the flat format that is then used for the
  servicebinding.io binding. In case of a json object value to a key,
  the value is represented as a json string.
* Existing bindings are not affected when upgrading Korifi. However,
  once the user updates the service instance credentials, the binding
  secret is rebuilt which causes statefulset restart. Further updates to
  the credentials will not cause additional restarts.
* We have created ADR 16 documenting how this works

fixes cloudfoundry#2900

Co-authored-by: Danail Branekov <[email protected]>
marsteg pushed a commit to marsteg/korifi that referenced this issue Mar 19, 2024
rather than map[string]string

Issue: cloudfoundry#2900

Co-authored-by: Danail Branekov <[email protected]>
Co-authored-by: Georgi Sabev <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

2 participants