-
Notifications
You must be signed in to change notification settings - Fork 36
Add enhancement proposal for generic constraints feature #91
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
Changes from 3 commits
f979265
8ab48ec
05be439
260bf1b
23c130f
15a6e8d
84335c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,181 @@ | ||||||
| --- | ||||||
| title: generic constraint | ||||||
| authors: | ||||||
| - "@dinhxuanvu" | ||||||
| reviewers: | ||||||
| - "@kevinrizza" | ||||||
| - "@joelandford" | ||||||
| approvers: | ||||||
| - "@kevinrizza" | ||||||
| creation-date: 2021-08-26 | ||||||
| last-updated: 2020-10-08 | ||||||
| status: provisional | ||||||
| --- | ||||||
|
|
||||||
| # generic-constraint | ||||||
|
|
||||||
| ## Release Signoff Checklist | ||||||
|
|
||||||
| - [ ] Enhancement is `implementable` | ||||||
| - [ ] Design details are appropriately documented from clear requirements | ||||||
| - [ ] Test plan is defined | ||||||
| - [ ] Graduation criteria for dev preview, tech preview, GA | ||||||
|
|
||||||
| ## Summary | ||||||
|
|
||||||
| The dependency resolution in OLM needs to support constraints that are based on arbitrary properties from either bundles or clusters beyond the existing GVK or package properties. | ||||||
|
|
||||||
| This enhancement proposes the adoption of [Common Expression Language | ||||||
| (CEL)](https://github.com/google/cel-go) to present the specification and usage of generic constraint that OLM can evaluate for dependency resolution at runtime. | ||||||
|
|
||||||
| CEL is a sufficiently lightweight expression language that has been adopted in multiple opensource projects. It allows users to express operator constraints in both straight-forward and advanced cases without OLM having to dictate the evaluation methodology. | ||||||
|
|
||||||
| ## Motivation | ||||||
|
|
||||||
| At the moment, OLM only supports 2 built-in types of constraint: | ||||||
| - GVK constraint (`olm.gvk.required`)* which depends on a specific GroupVersionKind information of a CRD/API. | ||||||
| - Package constraint (`olm.package.required`)* which depends a specific package name and version (or a range of version) | ||||||
|
|
||||||
| Both types are specifically handled by the resolver in OLM and cannot be changed or expanded to support further use cases. In order to support more types of constraints, it is generically expected for OLM to add more specific built-in types to allow users to express those new constraints in the bundles. Over the time, it may become cumbersome and impractical to continue to add more specific types to the resolver. The need for OLM to support a generic constraint model becomes more apparent. With a generic constraint model, OLM no longer needs to carry any specific evaluation methodology besides the understanding of how to parse the constraint syntax. The users should be able to express declaratively the rules for a constraint that can be evaluated againts the dataset of arbitrary properties. | ||||||
|
|
||||||
| * Note: The current GVK and package constraints are specified in `dependencies.yaml` using `olm.gvk` and `olm.package` type and they are coverted to `olm.gvk.required` or `olm.package.required` property (which is required constraint) to differentiate from the `olm.package` and `olm.gvk` property in the bundle. | ||||||
|
|
||||||
| ### Goals | ||||||
| - Provide specification and guideline to specify a constraint that contains declarative requirements/rules. | ||||||
| - Support the compound constraint (the constraint with mutliple requirements that is resolved into a single operator). | ||||||
| ### Non-Goals | ||||||
| - Eliminate the existing built-in constraints. | ||||||
| - Support multiple expression languages other than CEL (though the syntax may allow the support for other languages for future use). | ||||||
| - Provide mechanism to provide cluster constraints. This feature can be addressed in a separate enhancement. | ||||||
|
|
||||||
|
|
||||||
| ## Proposal | ||||||
|
|
||||||
| The [Common Expression Language (CEL)](https://github.com/google/cel-go) is a great inline expression language that can be used to express multiple rules within a single expression. The rules are fully customizable and constructed specifically for users' needs and requirements without OLM needing to know the design methodology. It is important to recoginize the dependent relationship between constraints and properties. As a constraint is evaluated against a dataset of properties from a bundle or a given source, in order to design a constraint, there must be an understanding on what properties are available in a given a bundle or source and what information they present in order for a constraint to be evaluated propertly. | ||||||
|
|
||||||
| The CEL library also supports [extension functions](https://github.com/google/cel-spec/blob/master/doc/langdef.md#extension-functions) which allows OLM to implement additional functions that may be necessary for certain operations. These functions then can be used within the CEL expression. For example, [semver](https://semver.org/) comparison is not built-in in CEL and it is often used in OLM as bundles are versioned in semver format. The semver functions can be added in the initial release and allow users to express semver evaluations in the constraint. However, extension functions should be treated and designed with care as they can be difficult to change in the future and potentially create version skew or breaking changes. | ||||||
|
|
||||||
| All CEL expressions are [parsed and typechecked](https://github.com/google/cel-go#parse-and-check) before evaluation. The insulting programs can be cached for later evaluation to reduce unnecesary computation. Also, the CEL evaluation is [thread-safe and side-effect free](https://github.com/google/cel-go#evaluate). | ||||||
|
||||||
| All CEL expressions are [parsed and typechecked](https://github.com/google/cel-go#parse-and-check) before evaluation. The insulting programs can be cached for later evaluation to reduce unnecesary computation. Also, the CEL evaluation is [thread-safe and side-effect free](https://github.com/google/cel-go#evaluate). | |
| All CEL expressions are [parsed and typechecked](https://github.com/google/cel-go#parse-and-check) before evaluation. The resulting programs can be cached for later evaluation to reduce unnecessary computation. Also, the CEL evaluation is [thread-safe and side-effect free](https://github.com/google/cel-go#evaluate). |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are all types prefixed by olm? shouldn't it be deppy or maybe nothing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should define at least one reserved prefix and document that custom properties should also use different unique prefixes (maybe owned domain?) to ensure uniqueness across the global set of properties that might exist. Uniqueness is especially important now that we're introducing a constraint syntax that supports arbitrary expressions against arbitrary properties.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of evaluator being an object with an id field rather than having evaluator being the string ID directly?
Do we envision needing to add other fields to the evaluator object? If so, what are some examples of those other fields that we know we'll need in the future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd go further: why not omit the evaluator entirely and identify via the type?
"type":"olm.constraint.cel",Same for action:
{
"type":"olm.constraint.cel.required",
"value": "properties.exists(p, p.type == \"certified\")",
}Edit:
Just noticed this form in the section at the end. Any reason it's not the preferred form?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern is that different root types will cause version skew concerns when we add a new constraint type.
If we:
- add support for
olm.constraint.cel.requiredin 0.20 - add support for
olm.constraint.foobar.requiredin 0.21
And then a bundle contains olm.constraint.foobar.required and is attempted to be installed in <=0.20, what does OLM do with that property?
In 0.20, support for that as a known constraint is not present, so OLM would treat it like an opaque property and ignore it entirely. There's no opportunity to fail resolution (if its actually required) or ignore it on purpose (because its optional) because OLM can't tell user intent since we're co-mingling constraints and properties.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this field apply only when evaluator.id is "cel"? If not, it we should describe the general purpose of this field and have a few examples (one of which should be a cel rule).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separate thread about rule...
- Can you go into some detail about what variables will be available in the context of an expression? Is it just properties?
- Are we planning to enable macros? If so, all or a subset?
- Are we planning to implement any custom functions? If so, what are they?
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question as I have for evaluator. Why do we need the sub id field? What are some examples of other fields that we know we'll need under the action object?
Will all actions be applicable to all future evaluators and all rules?
joelanford marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that #97 focuses on general compound constraints, it seems like our stance should be to favor that as a best practice. While the approach described here is just as valid from a technical perspective, it seems like the approach in #97 integrates better into how the resolver works and will give users more insight into which aspect(s) of the compound constraint caused resolution failures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This just seems like restating the constraint. Is this actually a field that should be optionally available on in the property metaschema?
{
"type": "proptype",
"value": <>,
"failureMessage": "custom message about this property that a user will see if it is unsatisfied"
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd buy "description" making sense for all properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the current state of this EP, I think I'd vote to start with failureMessage in the root of the olm.constraint value struct.
A general description and a constraint failureMessage seem different enough semantically to me that I don't think we should overload one onto the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.