Skip to content

Commit 3f4ece6

Browse files
jpbetzliggitt
andauthored
KEP-2876: Core library proposal for CEL (kubernetes#3132)
* Core library proposal for CEL * Apply feedback * update toc * Apply suggestions from code review Co-authored-by: Jordan Liggitt <[email protected]> * Apply feedback * Apply suggestions from code review Co-authored-by: Jordan Liggitt <[email protected]> * Apply suggestions from code review Co-authored-by: Jordan Liggitt <[email protected]> * Remove comments about has{prefix|suffix} since startsWith and endsWith are already available * Add strong wording around the implications of changes to the function library * Apply suggestions from code review Co-authored-by: Jordan Liggitt <[email protected]> * Clarify sum/min/max * fix TOC * Add regex find support, add notes about format support we considered Co-authored-by: Jordan Liggitt <[email protected]>
1 parent daa57ff commit 3f4ece6

File tree

2 files changed

+173
-19
lines changed

2 files changed

+173
-19
lines changed

keps/sig-api-machinery/2876-crd-validation-expression-language/README.md

Lines changed: 172 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- [Alternatives Evaluated](#alternatives-evaluated)
1818
- [Expression lifecycle](#expression-lifecycle)
1919
- [Function library](#function-library)
20+
- [Function Library Updates](#function-library-updates)
2021
- [User Stories](#user-stories)
2122
- [Notes/Constraints/Caveats (Optional)](#notesconstraintscaveats-optional)
2223
- [Risks and Mitigations](#risks-and-mitigations)
@@ -458,25 +459,176 @@ to fail with a descriptive error.
458459

459460
#### Function library
460461

461-
The function library available to expressions can be augmented using [extension
462-
functions](https://github.com/google/cel-spec/blob/master/doc/langdef.md#extension-functions).
462+
The functions will become VERY difficult to change as this feature matures.
463+
There are two types of risks here:
463464

464-
List of functions to include for the initial release:
465-
- Equality and Ordering (customized to do Kuberenetes semantic equality that handles "associative lists")
466-
- Regular Expressions
467-
- Some Standard Definitions
465+
- We add a function and later need to change it.
466+
- We don't add a function that is essential for use cases and later need to add it.
468467

469-
Considerations:
470-
- The functions will become VERY difficult to change as this feature matures. We
471-
should limit ourselves initially to functions that we have a high level of
472-
confidence will not need to be changed or rethought.
468+
Proposal:
469+
470+
- Keep all the CEL standard functions and macros as well as the extended string library.
471+
- Introduce a new "kubernetes" CEL extension library.
472+
- Add `isSorted` for lists with comparable elements. This is useful for ensuring that a list is kept in-order.
473+
- Add `sum` for lists of {int, uint, double, duration, string, bytes} (consistent with the [CEL _+_ operation](https://github.com/google/cel-spec/blob/master/doc/langdef.md#list-of-standard-definitions) )
474+
and add `min`, `max` for lists of {bool, int, uint, double, string, bytes, duration, timestamp} (consistent with the [CEL comparison operations](https://github.com/google/cel-spec/blob/master/doc/langdef.md#list-of-standard-definitions)).
475+
These operations can also be used in CEL on scalars by defining list literals
476+
inline , e.g. `[self.val1, self.val2].max()`. Overflow will raise the same error raised by arithmetic operation [overflow](https://github.com/google/cel-spec/blob/master/doc/langdef.md#overflow).
477+
- Add `indexOf` / `lastIndexOf` support for lists (overloading the existing string functions), this can be useful for
478+
validating partial order (i.e. the tasks of a workflow)
479+
- Add URL parsing: `url(string) URL`, `string(URL) string` (conversion), `getScheme(URL) string`, `getUserInfo(URL) string`,
480+
`getHost(URL) string`, `getPort(URL) string`, `getPath(URL) string`, `getQuery(URL) string`, `getFragment(URL) string`. This is
481+
useful for validating URLs. The go URL parser implements all the functionality.
482+
- Add regex `find(string) string` and `findAll(string) []string`. This makes it possible to parse out parts of string
483+
formats (we only provide timestamp, duration and url support directly).
484+
485+
The function libraries we need can be added using [extension
486+
functions](https://github.com/google/cel-spec/blob/master/doc/langdef.md#extension-functions) to either cel-go (if they accept our proposals)
487+
or directly to the Kubernetes codebase.
488+
489+
How we selected this function library:
490+
491+
First let's look at what CEL provides, and then look at what some of the core libraries of major programming
492+
languages provide, and see what notable absences there are.
493+
494+
CEL standard functions, defined in the [list of standard definitions](https://github.com/google/cel-spec/blob/master/doc/langdef.md#list-of-standard-definitions) include:
473495

474-
- Support kubernetes specific concepts, like accessing associative lists by key may be needed, but
475-
we need to review more use cases to determine if this is needed.
476-
477-
- The Kubernetes associated list equality uses map semantic equality which is different from CEL.
478-
We would consider overwriting in CEL or adding a workaround utility function.
496+
- `size` (or string, lists, maps, ...)
497+
- String inspection: `startsWith`, `endsWith`, `contains`
498+
- Regex: `matches`
499+
- Date/time accessors: `getDate`, `getDayOfMonth`, `getDayOfWeek`, `getDayOfYear`, `getFullYear`, `getHours`, `getMilliseconds`, `getMinutes`, `getMonth`, `getSeconds`
500+
501+
CEL standard [macros](https://github.com/google/cel-spec/blob/master/doc/langdef.md#macros) include:
502+
503+
- `has`
504+
- `all`
505+
- `exists`
506+
- `exists_one`
507+
- `map`
508+
- `filter`
509+
510+
CEL [extended string function library](https://github.com/google/cel-go/blob/master/ext/strings.go) includes:
511+
512+
- `charAt`
513+
- `indexOf`, `lastIndexOf`
514+
- `upperAscii`, `lowerAscii`
515+
- `replace` (string literal replacement, not regex)
516+
- `split`
517+
- `substring`
518+
- `trim`
519+
520+
Notable absences from the standard functions, when comparing against the core libraries of Go, Java and Python are:
521+
522+
Strings:
523+
524+
- trim/trimLeft/trimRight (trimming either by whitespace or by provided cutset)
525+
- trimPrefix/trimSuffix
526+
- join
527+
- replace (regex)
528+
- format (e.g. `fmt.Sprintf()`)
529+
530+
Lists:
531+
532+
- sort
533+
- indexOf/lastIndexOf
534+
- replace
535+
- sublist
536+
- split
537+
- reverse
538+
- sum/min/max (xref: [Rego function library](https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions))
539+
540+
Numbers:
541+
542+
- abs/cel/floor/round
543+
- exp/log/log10/logN/pow/sqrt
544+
- max(x, y, ..)/min(x, y, ...)
545+
- <trig functions>
546+
547+
Formats:
548+
549+
- semver: compare (xref [Kyverno function library](https://github.com/kyverno/kyverno/blob/main/pkg/engine/jmespath/functions.go))
550+
- urls: parsing out {scheme, userInfo, host, port, path, query, fragment}. This is [hard](https://community.helpsystems.com/forums/intermapper/miscellaneous-topics/5acc4fcf-fa83-e511-80cf-0050568460e4?_ga=2.113564423.1432958022.1523882681-2146416484.1523557976)
551+
to get right with regex, particularly with ipv6.
552+
- images: parsing out {name, tag, digest} as well as {host, port} from name and algorithm, hex} from digest. (xref: [grammar](https://github.com/distribution/distribution/blob/v2.7.1/reference/reference.go#L4-L24)).
553+
554+
Maps:
555+
556+
- <Ability to construct a map using a comprehension>
557+
558+
Some considerations when selecting which of the above we should include in CEL expressions in Kubernetes:
559+
560+
- We should include functions that will be needed for future use cases of CEL in Kubernetes (e.g. general admission) as
561+
ones needed for validation.
562+
- Regex replace is very powerful and useful. It is also potentially dangerous due to its ability to allocate memory.
563+
- `format`: Since CEL supports string concatenation, the value of having format would only be to do things like format floats.
564+
Formatting functions are complex and require extensive documentation to teach.
565+
- `isSorted` makes it possible to check if a list is sorted without the expense of performing a sort
566+
- `indexOf` / `lastIndexOf` / `split` / `replace` (the string functions) will be overloaded to provide the equivalent list functions.
567+
- `reduce` is going to be non-obvious to anyone that doesn't have a functional programming background?
568+
- If we provide `reduce` are developers going to also expect to have `fold` or `zip`?
569+
- "Ability to construct a map using a comprehension": this appears mechanically problematic to support in CEL?
570+
- None of the other policy libraries provide extended math/trig support and I asked Tristan (maintainer of CEL) if
571+
they are requested and he said "almost never", which he clarified to mean it has been requested exactly one time.
572+
- Average and quantile (median, 99th percentile, ...) and stddev are, however, often requested by other CEL users
573+
574+
Future work:
575+
576+
We've decided NOT to add the following functions. They may be useful for mutation, in which case we will consider adding them as part
577+
of any future work done involving CEL and mutating admission control:
578+
579+
- Regex replace
580+
- Add `trimPrefix` / `trimSuffix` for strings
581+
- Add `trim` / `trimLeft` / `trimRight` (overloaded to take an optional cutset arg) for strings
582+
- `sublist`, `split`, `replace` and `reverse` for lists (overloading existing string functions where appropriate)
583+
584+
##### Function Library Updates
479585

586+
Any changes to the CEL function library will make it possible for CRD authors to create CRDs that are incompatible with
587+
all previous Kubernetes releases that supported CEL. Because of this incompatibility, changing the function library
588+
will need to carefully considered and kept to a minimum. All changes will need to be limited to function additions.
589+
We will not add functions to do things that can already be reasonably accomplished with existing functions Improving ease-of-use
590+
at the cost of fragmentation / incompatibility with older servers is not a good trade-off.
591+
592+
Any function library change must follow the [API Changes](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api_changes.md)
593+
guidelines. Since a change to the function library is a change to the `x-kubernetes-validations.rule` field, it must be
594+
introduced one Kubernetes release prior to when it may be included in create/update requests to CRDs. Specifically:
595+
596+
- Kubernetes Version N-1: Entirely unaware of the library update.
597+
- Kubernetes Version N: Supports CRD that use library updates, but does not allow the library updates to used when
598+
setting `x-kubernetes-validations.rule` fields.
599+
- Kubernetes Version N+1: Library update fully enabled.
600+
601+
The mechanism for this will be:
602+
603+
- All new functions, macros, or overloads of existing functions, will be added to a separate "future compatibility" CEL
604+
extension library (which extension library a function is in is an internal detail that is not visible to users).
605+
- For create requests, and for any CEL expressions that are changed as part of an update, the CEL expression will be
606+
compiled **without** the "future compatibility" CEL extension library.
607+
- For CEL expressions not changed in an update, the CEL expression will be compiled **with** the
608+
"kubernetes-future-compatibility" CEL extension. This ensures that persisted fields that already use the change continue
609+
to compile.
610+
- The "future compatibility" CEL extension library will always be included when CEL expressions are evaluated.
611+
- When the next version of Kubernetes is release, the library functions will be moved from "future compatibility"
612+
- to the main CEL extension library we use to extend CEL for Kubernetes.
613+
614+
Alternatives considered:
615+
616+
Versioning Alternative:
617+
618+
- All changes to CEL (core language and libraries) will be versioned and CEL expressions must specify the version they
619+
are compatible with.
620+
- All versions must be backward compatible (a newer version of CEL is compatible with all older versions of CEL)
621+
- The API server rejects requests containing CEL versions newer than it supports.
622+
623+
Pros:
624+
625+
- Very clear what CRDs are compatible with what Kubernetes versions.
626+
627+
Cons:
628+
629+
- Validating that a CEL expression is the version it claims to be is difficult for older version of CEL. Either the API
630+
server would need to contain multiple versions of the CEL compiler, or it would need a compiler that can be configured to
631+
compile a CEL expression according to the language rules of an older version.
480632

481633
### User Stories
482634

@@ -596,6 +748,8 @@ for our use case.
596748
The good news is that https://github.com/google/cel-policy-templates-go already has
597749
demonstrated integrating CEL with OpenAPIv3. We plan to leverage this work.
598750

751+
Support for comparing integers and floats is supported in beta and later (in alpha, ints could only be compared to ints and floats to floats).
752+
599753
We will add detailed test coverage for numeric comparisons due to
600754
[google/cel-spec#54](https://github.com/google/cel-spec/issues/54#issuecomment-491464172) including
601755
coverage of interactions in these dimensions:
@@ -611,12 +765,12 @@ coverage of interactions in these dimensions:
611765
Types:
612766

613767
| OpenAPIv3 type | CEL type |
614-
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
768+
|----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
615769
| 'object' with Properties | object / "message type" (`type(<object>)` evaluates to `selfType<uniqueNumber>.path.to.object.from.self` |
616770
| 'object' with AdditionalProperties | map |
617771
| 'object' with x-kubernetes-embedded-type | object / "message type", 'apiVersion', 'kind', 'metadata.name' and 'metadata.generateName' are implicitly included in schema |
618772
| 'object' with x-kubernetes-preserve-unknown-fields | object / "message type", unknown fields are NOT accessible in CEL expression |
619-
| x-kubernetes-int-or-string | dynamic object that is either an int or a string, `type(value)` can be used to check the type |
773+
| x-kubernetes-int-or-string | union of int or string, `self.intOrString < 100 \|\| self.intOrString == '50%'` evaluates to true for both `50` and `"50%"` |
620774
| 'array | list |
621775
| 'array' with x-kubernetes-list-type=map | list with map based Equality & unique key guarantees |
622776
| 'array' with x-kubernetes-list-type=set | list with set based Equality & unique entry guarantees |
@@ -896,7 +1050,7 @@ to minimize negative impact to ecosystem.
8961050

8971051
See also the future plans section for this. We believe that CEL for General Admission Control is
8981052
valuable and should be implemented. We are implementing CRD validation with CEL first because is is
899-
a more constrainted problem and is complementary to CEL for general admission (even if we already
1053+
a more constrained problem and is complementary to CEL for general admission (even if we already
9001054
had CEL for general admission implemented, the convenience of inline CEL validation expressions in
9011055
CRDs is sufficiently convenient to justify it being added).
9021056

keps/sig-api-machinery/2876-crd-validation-expression-language/kep.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors:
66
- "@DangerOnTheRanger"
77
- "@leilajal"
88
owning-sig: sig-api-machinery
9-
status: implemented
9+
status: implementable
1010
creation-date: 2021-05-26
1111
reviewers:
1212
- "@deads2k"

0 commit comments

Comments
 (0)