design/one-pager-fn-claim-conditions.md
Composition Function authors should be able to communicate and translate the underlying status with users.
We think authors often won't want to surface the status as it appear on an MR, but will probably want to derive more user-friendly messages from it. Messages that are more meaningful to folks reading claims.
Some examples include:
We think authors may want to have a catch-all Internal Error message. Authors should be able to display the real error on the XR and provide a basic "Internal Error" message on the Claim.
Currently internal errors often leave the Claim in a "Waiting" state. It would be nice to notify the user that an internal error was encountered, and that the team has been notified by an alert.
Currently functions can return Results. Depending on the type of results seen, you can observe the following behavior on the Composite Resource.
Fatal Result:
Warning Result:
Normal Result:
Currently the only path to communicate a custom message with the user is by defining your own field in the Claim's status. For example, we can define an XRD with:
status:
someCommunicationField:
- msg: "Something went wrong."
There are a couple issues with this solution.
response.Fatal, however, this does not also allow us to update the XR and
Claim for communication with the user.status.conditions).Currently you can update the Composite's status conditions by setting them with SetDesiredCompositeResource. There are a couple of limitations to this:
Example of setting the Composite's status conditions.
// includes:
// corev1 "k8s.io/api/core/v1"
// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
//
// "github.com/crossplane/function-sdk-go/response"
desiredXR, err := request.GetDesiredCompositeResource(req)
c := xpv1.Condition{
Type: xpv1.ConditionType("ImageReady"),
Status: corev1.ConditionFalse,
LastTransitionTime: metav1.Now(),
Reason: "NotFound",
Message: "The image provided does not exist or you are not "+
"authorized to use it.",
}
desiredXR.Resource.SetConditions(c)
response.SetDesiredCompositeResource(rsp, desiredXR)
We would like to allow the Composition Function author to:
The following sections get into the details of each of the above items.
Currently each result returned by a function will create a corresponding event on the XR (if no previous fatal result exists).
We can expand this functionality by allowing the Result to have targets. In order to accomplish this, we will need to expand the Result API as follows.
message Result {
// Omitted for brevity
Target target = 3;
}
// Target of Function results.
enum Target {
TARGET_UNSPECIFIED = 0;
TARGET_COMPOSITE_ONLY = 1;
TARGET_COMPOSITE_AND_CLAIM = 2;
}
The reason for having TARGET_COMPOSITE_AND_CLAIM and not TARGET_CLAIM is an
implementation limitation. This prevents more involved API changes, and this
is also consistent with existing behavior (func copies to XR, Crossplane copies
XR to Claim).
The following is an example of how a function author could use this behavior. Note that this is just a sketch and may not be the final API.
// import "github.com/crossplane/function-sdk-go/response"
response.Fatal(rsp, errors.New("The image provided does not exist or you are not authorized to use it.")).
ConditionFalse("ImageReady", "NotFound").
TargetCompositeAndClaim()
To support this behavior, the status of the Composite would need an additional
field claimConditions. This field will contain the types of conditions that
should be propagated to the Claim.
# composite status
status:
# The XR's condition types that should be back-propagated to the claim
claimConditions: [DatabaseReady, ImageReady]
# The XR's conditions
conditions:
- type: DatabaseReady
status: True
reason: Available
- type: ImageReady
status: False
reason: NotFound
message: The image provided does not exist or you are not authorized to use it.
We would like the function author to be able to set the Claim's status conditions. This would allow the function author to clearly communicate the state of the Claim with their users.
To allow the setting of conditions in the result, we will need to expand the Result API as follows.
message Result {
// Omitted for brevity
// Optionally update the supplied status condition on all targets.
// The result's reason and message will be used in the condition.
optional Condition condition = 4;
}
message Condition {
// Type of the condition, e.g. DatabaseReady.
// 'Ready' and 'Synced' are reserved for use by Crossplane.
string type = 1;
// Status of the condition.
Status status = 2;
// Machine-readable PascalCase reason.
string reason = 3;
}
An example of a function utilizing this new ability:
// rb "github.com/crossplane/function-sdk-go/response/result/builder"
// const databaseReady = "DatabaseReady"
// const reasonUnauthorized = "Unauthorized"
// var messageUnauthorized = errors.New("You are unauthorized to access this resource.")
result := rb.Fatal(messageUnauthorized).
TargetCompositeAndClaim().
WithConditionFalse(databaseReady, reasonUnauthorized).
Build()
response.AddResult(rsp, result)
Lets say we are a team of platform engineers who have a Crossplane offering. For each Claim, we wish to expose a set of conditions that users can expect to exist which provide:
Lets say we have a claim that does the following..
Given a few different scenarios, users could expect to see the following
status.conditions for the claim.
First we found the database and determined that the user has authorization, however, the image they provided was not found.
An example of the Claim's status:
status:
conditions:
- type: DatabaseReady
status: True
reason: Available
- type: ImageReady
status: False
reason: NotFound
message: The image provided does not exist or you are not authorized to use
it.
All is fine and the application is progressing but not yet fully online.
An example of the Claim's status:
status:
conditions:
- type: DatabaseReady
status: True
reason: Available
- type: ImageReady
status: True
reason: Available
- type: AppReady
status: False
reason: Creating
message: Waiting for the deployment to be available.
Once everything is online and running smoothly, users should see something like this.
An example of the Claim's status:
status:
conditions:
- type: DatabaseReady
status: True
reason: Available
- type: ImageReady
status: True
reason: Available
- type: AppReady
status: True
reason: Available