Skip to content

Commit 97d0ccf

Browse files
authored
Add support for dynamic expression Secret and ConfigMap outputs (#4398)
* Add support for dynamic expression Secret and ConfigMap outputs * Add generator support for dynamic configmap and secret export. * Add webhook validation for new operatorSpec fields. * Add generic controller integration to output expressions to ConfigMap/Secret. * Add CEL tests * Fix a few issues with CEL expression parsing. * Regenerate api * Add CEL Expressions documentation
1 parent 015ae07 commit 97d0ccf

File tree

1,586 files changed

+174634
-2899
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,586 files changed

+174634
-2899
lines changed
+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
---
2+
title: CEL Expressions
3+
linktitle: expressions
4+
weight: 1 # This is the default weight if you just want to be ordered alphabetically
5+
---
6+
7+
## Expressions in ASO
8+
9+
ASO offers some support for [Common Expression Language (CEL)](https://github.com/google/cel-spec) expressions.
10+
11+
Expressions in ASO can be used to build your own Secret or ConfigMap output. This helps to solve problems like:
12+
13+
- Applications that need connection strings with specific (possibly non-standard) formats.
14+
- Exporting arbitrary fields from a resource for use by other applications.
15+
- Exporting static text alongside dynamic data.
16+
17+
CEL expressions are currently supported on most resources via the `spec.operatorSpec.configMapExpressions` and
18+
`spec.operatorSpec.secretExpressions` fields, which allow configuring expressions output
19+
to ConfigMaps and Secrets, respectively.
20+
21+
### Inputs
22+
23+
Expressions have access to the following inputs:
24+
25+
| Type | Variable(s) |
26+
|-----------|------------------|
27+
| ConfigMap | `self` |
28+
| Secret | `self`, `secret` |
29+
30+
`self`: The resource itself.
31+
32+
{{% alert title="Note" %}}
33+
`self` represents the resource at the version you last applied the resource at. So for example if `v1api20200101`
34+
has a `self.spec.foo` field that has been _removed_ in `v1api20220101`, you can still refer to `self.spec.foo`
35+
in your expressions as long as you're `kubectl apply`-ing the resource with the `v1api20200101` version.
36+
37+
This also holds for new fields, you can refer to newly added fields _only in the API versions that have those fields_.
38+
{{% /alert %}}
39+
40+
`secret`: A set of secrets associated with the resource. The specific secrets supported vary by resource type,
41+
but can be found on the `spec.operatorSpec.secrets` type.
42+
For example, [eventhub Namespace](https://azure.github.io/azure-service-operator/reference/eventhub/v1api20211101/#eventhub.azure.com/v1api20211101.NamespaceOperatorSecrets)
43+
supports the following 4 secrets:
44+
- `primaryConnectionString`
45+
- `primaryKey`
46+
- `secondaryConnectionString`
47+
- `secondaryKey`
48+
49+
### Outputs
50+
51+
Expressions for `spec.operatorSpec.configMapExpressions` and `spec.operatorSpec.secretExpressions`
52+
must output `string` or `map[string]string` data. Any other expression output type will be rejected.
53+
54+
### CEL options, language features, and libraries
55+
56+
Compare with [Kubernetes supported options](https://kubernetes.io/docs/reference/using-api/cel/#cel-options-language-features-and-libraries)
57+
58+
| Feature |
59+
|------------------------------------------------------------------------------------------------------------------|
60+
| [Standard macros](https://github.com/google/cel-spec/blob/master/doc/langdef.md#macros) |
61+
| [Standard functions](https://github.com/google/cel-spec/blob/master/doc/langdef.md#list-of-standard-definitions) |
62+
| [Default UTC Time Zone](https://pkg.go.dev/github.com/google/cel-go/cel#DefaultUTCTimeZone) |
63+
| [Eagerly Validate Declarations](https://pkg.go.dev/github.com/google/cel-go/cel#EagerlyValidateDeclarations) |
64+
| [Extended String Library, version 3](https://pkg.go.dev/github.com/google/cel-go/ext#Strings) |
65+
| [Optional Types](https://pkg.go.dev/github.com/google/cel-go/cel#OptionalTypes) |
66+
| [Cross Type Numeric comparisons](https://pkg.go.dev/github.com/google/cel-go/cel#CrossTypeNumericComparisons) |
67+
68+
69+
### Escaping
70+
71+
ASO follows the [same escaping rules as Kubernetes](https://kubernetes.io/docs/reference/using-api/cel/#escaping),
72+
except we don't escape CEL keywords.
73+
74+
While escaping isn't required for most properties, CEL doesn't support the `.`, `-`, or `/` characters in expressions, so
75+
properties whose names contain these characters must be escaped. The escaping rules are:
76+
77+
| Escape sequence | Property name equivalent | Unescaped example | Escaped example |
78+
|-------------------|----------------------------------------------------------------------------------------------------------------------|-------------------|--------------------------|
79+
| \_\_underscores__ | __ | my__field | my\_\_underscores__field |
80+
| \_\_dot__ | . | my.field | my\_\_dot__field |
81+
| \_\_dash__ | - | my-field | my\_\_dash__field |
82+
| \_\_slash__ | / | my/field | my\_\_slash__field |
83+
| \_\_{keyword}__ | [CEL RESERVED keyword (false, true, in, null)](https://github.com/google/cel-spec/blob/master/doc/langdef.md#syntax) | false | \_\_false__ |
84+
85+
### Examples
86+
87+
These examples are all written for a simple
88+
[cache.Redis](https://azure.github.io/azure-service-operator/reference/cache/v1api20230801/#cache.azure.com/v1api20230801.Redis).
89+
There's nothing special about Redis it's just used as an example.
90+
91+
In each of these examples, we start with the given Redis and apply the expression as mentioned in the example itself.
92+
93+
It might be useful to refer to
94+
the [DestinationExpression](https://pkg.go.dev/github.com/Azure/azure-service-operator/v2/pkg/genruntime/core#DestinationExpression)
95+
documentation to understand what the `name`/`key`/`value` fields are doing in all of these examples.
96+
97+
For more advanced examples, refer to the
98+
[CEL language definition](https://github.com/google/cel-spec/blob/master/doc/langdef.md).
99+
100+
**Sample Redis:**
101+
```yaml
102+
apiVersion: cache.azure.com/v1api20230801
103+
kind: Redis
104+
metadata:
105+
name: sampleredis1
106+
namespace: default
107+
annotations:
108+
foo: bar
109+
baz: qux
110+
spec:
111+
location: westus2
112+
owner:
113+
name: aso-sample-rg
114+
sku:
115+
family: P
116+
name: Premium
117+
capacity: 1
118+
enableNonSslPort: false
119+
minimumTlsVersion: "1.2"
120+
redisConfiguration:
121+
maxmemory-delta: "10"
122+
maxmemory-policy: allkeys-lru
123+
redisVersion: "6"
124+
zones:
125+
- "1"
126+
- "2"
127+
- "3"
128+
```
129+
130+
{{% alert title="Note" %}}
131+
Just to keep it simple, the sample YAML doesn't include status. Real resources have status, and you can build your
132+
CEL expressions with `self.status` in addition to `self.spec` or `self.metadata`.
133+
{{% /alert %}}
134+
135+
#### A full example
136+
137+
Expression snippet:
138+
```yaml
139+
spec:
140+
operatorSpec:
141+
configMapExpressions:
142+
- name: my-configmap
143+
key: location
144+
value: self.spec.location
145+
```
146+
147+
Output (ConfigMap):
148+
```yaml
149+
metadata:
150+
name: my-configmap
151+
data:
152+
location: westus2
153+
```
154+
155+
From here on, we'll elide the supporting YAML of the above example and just focus on the CEL expression input (`value`)
156+
and the resulting output, still based on the example `Redis` above.
157+
158+
| Description | Expression | Result |
159+
|--------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
160+
| Hardcoded string | `"helloworld"` | `helloworld` |
161+
| Formatted string | `"%s:%d".format([self.spec.location, 7])` | `westus2:7` |
162+
| String math | `self.metadata.namespace + ":" + self.metadata.name` | `default:sampleredis1` |
163+
| Int output (error) | `self.spec.sku.capacity` | Error, expression "self.spec.sku.capacity" must return one of [string,map(string, string)], but was int |
164+
| Coerce to string | `string(self.spec.sku.capacity)` | `1` |
165+
| Map output | `self.metadata.annotations` | `{"foo": "bar", "baz": "qux"}` |
166+
| Array macro | `self.spec.zones.filter(a, int(a) % 2 == 0).join("-")` | `2-4` |
167+
| Select array item | `self.spec.zones[0]` | `1` |
168+
| Select map item | `self.metadata.annotations["foo"]` | `bar` |

0 commit comments

Comments
 (0)