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

Document requirements to make functions with requirements backward compatible #801

Open
negz opened this issue Feb 16, 2024 · 4 comments
Open
Labels
docs User docs related issues and content enhancement New feature or request functions

Comments

@negz
Copy link
Member

negz commented Feb 16, 2024

What problem are you facing?

While responding to crossplane/crossplane#5399 I realized the new extra resources feature of Crossplane needs some care to be backward compatible.

Let's define:

  • Forward compatible: a function built using the Crossplane v1.14 protobuf works with v1.15 and above.
  • Backward compatible: a function build using the Crossplane v1.15 protobuf works with v1.14 and earlier.

When I say "works" I mean that all features of the lowest common version work as normal:

  • Crossplane v1.15 and a function built against the v1.14 protobuf should work and be able to use all v1.14 features.
  • Crossplane v1.14 and a function built against the v1.15 protobuf should work and be able to use all v1.14 features.

The extra resources feature is forward compatible. A function built using the v1.14 protobuf will never set requirements in its RunFunctionResponse, and will never expect Crossplane to respond with extra_resources. Crossplane won't include them, and even if it did the function would just ignore them. This is great, because it means existing functions will automatically work with v1.15.

The feature is potentially not backward compatible. It requires good function behavior to be backward compatible.

A function built using the v1.15 protobuf could set requirements to ask for extra resources. When it does this, it expects Crossplane to communicate that resolution of requirements is complete by sending a second RunFunctionRequest with the extra_resources field set:

message RunFunctionRequest {
  // Existing fields omitted for brevity

  // Note that extra resources is a map to Resources, plural.
  // The map key corresponds to the key in a RunFunctionResponse's
  // extra_resources field. If a Function requests extra resources that
  // don't exist Crossplane sets the map key to an empty Resources
  // message to indicate that it attempted to satisfy the request.
  map<string, Resources> extra_resources = 3;
}

You could imagine functions that want extra resources having a condition like:

  • If extra_resources not in RunFunctionRequest, send RunFunctionResponse with requirements set.
  • Else use extra_resources from RunFunctionRequest to set desired in RunFunctionResponse.

Crossplane v1.14 will never set extra_resources. So from the function's perspective it's going to look like Crossplane is constantly sending it the initial RunFunctionRequest in the resolution process. It will set requirements and expect to be called again with extra_resources, but that will never happen. Instead Crossplane is going to ignore its requirements and process its desired state.

How could Crossplane help solve your problem?

I think as long as functions with requirements degrade gracefully if extra_resources are never sent, they're backward compatible. https://github.com/crossplane-contrib/function-environment-configs/blob/0c9cf192/fn.go#L71 is a good example of this.

If you try to use function-environment-configs with Crossplane v1.14:

  1. Crossplane will send a RunFunctionRequest.
  2. The function will send a RunFunctionResponse with requirements, but (critically) also pass through any existing desired state unmodified (thanks to its use of response.To).
  3. Crossplane will ignore the requirements and process the desired state.

This means the function will just be a no-op with Crossplane v1.14. Any subsequent functions will run as if there were no environment configs found.

I think we should explicitly document that a function should:

  • Always pass through desired in its RunFunctionResponse when it returns requirements.
  • If appropriate, don't wait for requirements to be satisfied to return desired state.

This avoids two possible mistakes:

  • A function not passing through desired when it returns requirements, because it expects Crossplane to ignore desired until requirements have been processed. Crossplane v1.14 would interpret this as "delete all composed resources".
  • A function with optional requirements never updating desired until it sees that its requirements has been processed. Such a function would always be a no-op with Crossplane v1.14, even though it need not be.
@negz negz added enhancement New feature or request functions docs User docs related issues and content labels Feb 16, 2024
Copy link

Crossplane does not currently have enough maintainers to address every issue and pull request. This issue has been automatically marked as stale because it has had no activity in the last 90 days. It will be closed in 14 days if no further activity occurs. Leaving a comment starting with /fresh will mark this issue as not stale.

@negz
Copy link
Member Author

negz commented May 17, 2024

/fresh

Copy link

Crossplane does not currently have enough maintainers to address every issue and pull request. This issue has been automatically marked as stale because it has had no activity in the last 90 days. It will be closed in 14 days if no further activity occurs. Leaving a comment starting with /fresh will mark this issue as not stale.

@negz
Copy link
Member Author

negz commented Aug 22, 2024

/fresh

@negz negz transferred this issue from crossplane/crossplane Aug 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs User docs related issues and content enhancement New feature or request functions
Projects
None yet
Development

No branches or pull requests

1 participant