Skip to content

Commit 49edd50

Browse files
Add info on expression composition.
1 parent 9d63597 commit 49edd50

File tree

1 file changed

+67
-6
lines changed
  • keps/sig-api-machinery/3488-cel-admission-control

1 file changed

+67
-6
lines changed

keps/sig-api-machinery/3488-cel-admission-control/README.md

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- [Enforcement Actions](#enforcement-actions)
2929
- [Namespace scoped policy binding](#namespace-scoped-policy-binding)
3030
- [CEL Expression Composition](#cel-expression-composition)
31+
- [Use Cases](#use-cases)
3132
- [Variables](#variables)
3233
- [Secondary Authz](#secondary-authz)
3334
- [Access to namespace metadata](#access-to-namespace-metadata)
@@ -1065,28 +1066,88 @@ Details to consider:
10651066

10661067
#### CEL Expression Composition
10671068

1069+
##### Use Cases
1070+
1071+
###### Code re-use for complicated expressions
1072+
1073+
A CEL expression may not be computationally expensive, but could still be
1074+
intricate enough that copy-pasting could prove to be a bad decision later on
1075+
in time. With the addition of the `messageExpression` field, more copy-pasting
1076+
is expected as well. If a sufficiently complex expression ended up copy-pasted everywhere,
1077+
and then needs to be updated somehow, it will need that update in every place
1078+
it was copy-pasted. A variable, on the other hand, will only need to be updated
1079+
in one place.
1080+
1081+
###### Reusing/memoizing an expensive computation
1082+
1083+
For a CEL expression that runs in O(n^2) time or worse (or otherwise
1084+
takes a significant amount of time to execute), it would be nice to only run
1085+
it when necessary. For instance, if multiple validation expressions used the
1086+
same expensive expression, that expression could be refactored out into a
1087+
variable.
1088+
10681089
##### Variables
10691090

1070-
Each CEL "program" is a single expression. There is no support for vaiable
1091+
Each CEL "program" is a single expression. There is no support for variable
10711092
assignment. This can result in redundant code to traverse maps/arrays or
10721093
dereference particular fields.
10731094

10741095
We can support this in much the same way as cel-policy-template `terms`. These
10751096
can be lazily evaluated while the validation expressions are evaluated
1076-
(cel-policy-template does this). The results can also be memoized to avoid
1077-
repeated evaluations if they are shared across validations.
1097+
(cel-policy-template does this).
1098+
1099+
A policy can include an additional `variables` section. This is an array
1100+
containing one or more `name` and `expression` pairs, which can be used/re-used by
1101+
the policy's validation expressions. These results are memoized on a
1102+
per-validation basis, so if multiple expressions use the same spec variables,
1103+
the expression that calculates the variable's value will only run once.
1104+
1105+
The variables can be accessed as members of `variables`, which is an object
1106+
that is exposed to CEL expressions (both validation expressions as well as
1107+
other variables).
1108+
1109+
For example:
10781110

10791111
```yaml
10801112
variables:
10811113
- name: metadataList
10821114
expression: "spec.list.map(x, x.metadata)"
10831115
- name: itemMetadataNames
1084-
expression: "metadataList.map(m, m.name)"
1116+
expression: "variables.metadataList.map(m, m.name)"
10851117
validations:
1086-
- expression: "itemMetadataNames.all(name, name.startsWith('xyz-'))"
1087-
- expression: "itemMetadataNames.exists(name, name == 'required')"
1118+
- expression: "variables.itemMetadataNames.all(name, name.startsWith('xyz-'))"
1119+
- expression: "variables.itemMetadataNames.exists(name, name == 'required')"
10881120
```
10891121

1122+
Variable names must be valid CEL names. What constitutes a
1123+
valid CEL name can be found at CEL's [language definition](https://github.com/google/cel-spec/blob/master/doc/langdef.md#syntax) under `IDENT`.
1124+
This validity is checked at write time.
1125+
1126+
For per-policy runtime cost limit purposes, variables count towards the runtime cost limit
1127+
once per policy. The cost of each variable is computed when it is first evaluated in an
1128+
expression, mirroring how the cost limit would be calculated if the variable's
1129+
expression was embedded verbatim. If the runtime cost limit is exceeded in the
1130+
process, then evaluation halts. No individual variable or expression will be listed as the
1131+
cause in the resulting message. Whether or not the request actually fails depends on the failure policy,
1132+
however. For subsequent uses, inclusion of the variable has zero effect on the runtime
1133+
cost limit. If the variable evaluates to an array or some other iterable, and some expression
1134+
iterates on it, that of course contributes to the cost limit, but simply including the variable does
1135+
not add the underlying expression's cost again.
1136+
1137+
Variables are also subject to the per-expression runtime cost limit. Exceeding the per-expression
1138+
runtime cost limit is always attributed to the variable, unlike the per-policy limit.
1139+
1140+
Variables can only reference other variables that
1141+
have been previously defined in the `variables` section, so circular references
1142+
are not allowed.
1143+
1144+
If an error ocurrs during variable evaluation, then the expression
1145+
that caused it to be evaluated (since variable are always
1146+
lazily-evaluated) also finishes with an error. Evaluation for that
1147+
variable is not attempted again during the same validation; if any other
1148+
expressions attempt to evaluate a variable that already failed an evaluation
1149+
attempt, they will also be considered to have failed.
1150+
10901151
#### Secondary Authz
10911152

10921153
We have general agreement to include this as a feature, but need to provide

0 commit comments

Comments
 (0)