|
33 | 33 | optionValues: |
34 | 34 | - name: perses.sidecar.enabled |
35 | 35 | value: true |
| 36 | + - name: perses.ingress.host |
| 37 | + expression: | |
| 38 | + "perses.${global.greenhouse.clusterName}.example.com" |
36 | 39 | pluginDefinitionRef: |
37 | 40 | kind: ClusterPluginDefinition |
38 | 41 | name: perses |
@@ -86,8 +89,283 @@ spec: |
86 | 89 |
|
87 | 90 | `.spec.deletionPolicy` is an optional field that specifies the behaviour when a PluginPreset is deleted. The possible values are `Delete` and `Retain`. If set to `Delete` (the default), all Plugins created by the PluginPreset will also be deleted when the PluginPreset is deleted. If set to `Retain`, the Plugins will remain after the PluginPreset is deleted or if the Cluster stops matching the selector. |
88 | 91 |
|
| 92 | +## CEL Expressions in OptionValues |
| 93 | + |
| 94 | +PluginPresets support CEL (Common Expression Language) expressions in `optionValues`. Expressions are evaluated during PluginPreset reconciliation, and the resulting Plugin contains only the resolved values with no expression fields remaining. |
| 95 | + |
| 96 | +Expressions use the `${...}` syntax to reference dynamic values: |
| 97 | + |
| 98 | +```yaml |
| 99 | +spec: |
| 100 | + plugin: |
| 101 | + optionValues: |
| 102 | + - name: app.hostname |
| 103 | + expression: | |
| 104 | + "myapp.${global.greenhouse.clusterName}.example.com" |
| 105 | +``` |
| 106 | + |
| 107 | +When this PluginPreset creates a Plugin for a cluster named `cluster-a`, the Plugin will contain: |
| 108 | + |
| 109 | +```yaml |
| 110 | +spec: |
| 111 | + optionValues: |
| 112 | + - name: app.hostname |
| 113 | + value: "myapp.cluster-a.example.com" |
| 114 | +``` |
| 115 | + |
| 116 | +### Available Variables |
| 117 | + |
| 118 | +| Variable | Description | Example Value | |
| 119 | +|--------------------------------------|----------------------------|------------------------------| |
| 120 | +| `global.greenhouse.clusterName` | Name of the target cluster | `cluster-a` | |
| 121 | +| `global.greenhouse.organizationName` | Organization namespace | `my-org` | |
| 122 | +| `global.greenhouse.clusterNames` | List of all cluster names | `["cluster-a", "cluster-b"]` | |
| 123 | +| `global.greenhouse.teamNames` | List of all team names | `["team-1", "team-2"]` | |
| 124 | +| `global.greenhouse.baseDomain` | Base DNS domain | `greenhouse.example.com` | |
| 125 | +| `global.greenhouse.metadata.*` | Cluster metadata labels | `eu-de-1` | |
| 126 | + |
| 127 | +> :information_source: `global.greenhouse.metadata.*` values are derived from cluster labels prefixed with `metadata.greenhouse.sap/`. For example, the label `metadata.greenhouse.sap/region: eu-de-1` becomes available as `global.greenhouse.metadata.region`. |
| 128 | + |
| 129 | +### Examples |
| 130 | + |
| 131 | +**Hostname per cluster:** |
| 132 | + |
| 133 | +```yaml |
| 134 | +- name: ingress.host |
| 135 | + expression: | |
| 136 | + "service.${global.greenhouse.clusterName}.example.com" |
| 137 | +# Result for cluster "cluster-a": "service.cluster-a.example.com" |
| 138 | +``` |
| 139 | + |
| 140 | +**Using cluster metadata:** |
| 141 | + |
| 142 | +```yaml |
| 143 | +- name: ingress.host |
| 144 | + expression: | |
| 145 | + "service.${global.greenhouse.metadata.region}.example.com" |
| 146 | +# Result: "service.eu-de-1.example.com" |
| 147 | +# Requires label metadata.greenhouse.sap/region on the cluster |
| 148 | +``` |
| 149 | + |
| 150 | +**Combining variables:** |
| 151 | + |
| 152 | +```yaml |
| 153 | +- name: app.fqdn |
| 154 | + expression: | |
| 155 | + "${global.greenhouse.clusterName}-${global.greenhouse.organizationName}" |
| 156 | +# Result for cluster "cluster-a" in org "my-org": "cluster-a-my-org" |
| 157 | +``` |
| 158 | + |
| 159 | +### Expressions in ClusterOptionOverrides |
| 160 | + |
| 161 | +Expressions can also be used in `clusterOptionOverrides`. Overrides are merged before expression evaluation, so override expressions are also resolved: |
| 162 | + |
| 163 | +```yaml |
| 164 | +spec: |
| 165 | + plugin: |
| 166 | + optionValues: |
| 167 | + - name: app.mode |
| 168 | + value: "standard" |
| 169 | + clusterOptionOverrides: |
| 170 | + - clusterName: special-cluster |
| 171 | + overrides: |
| 172 | + - name: app.hostname |
| 173 | + expression: | |
| 174 | + "special.${global.greenhouse.metadata.region}.example.com" |
| 175 | +``` |
| 176 | + |
| 177 | +> :information_source: Expressions are only evaluated in PluginPresets. |
| 178 | + |
| 179 | +> :warning: CEL expressions on standalone Plugins are deprecated and will be removed in a future release. Use PluginPresets for expression evaluation. |
| 180 | + |
| 181 | + |
| 182 | +## Feature Flag |
| 183 | + |
| 184 | +CEL expression evaluation in PluginPresets is enabled only when the feature flag `pluginPreset.expressionEvaluationEnabled` is set to `true` in the Greenhouse feature flags ConfigMap. |
| 185 | + |
| 186 | + By default, this flag is `false` if it is unset or invalid, and expressions are passed through as literal values without evaluation. |
| 187 | + |
| 188 | +```yaml |
| 189 | +# greenhouse-feature-flags ConfigMap |
| 190 | +apiVersion: v1 |
| 191 | +kind: ConfigMap |
| 192 | +metadata: |
| 193 | + name: greenhouse-feature-flags |
| 194 | + namespace: greenhouse |
| 195 | +data: |
| 196 | + pluginPreset: | |
| 197 | + expressionEvaluationEnabled: true |
| 198 | +``` |
| 199 | + |
| 200 | +## ValueFrom References Between PluginPresets |
| 201 | + |
| 202 | +PluginPresets can reference option values from other PluginPresets using `valueFrom.ref`. |
| 203 | +This enables one PluginPreset to use resolved values from another, including values generated by CEL expressions. |
| 204 | + |
| 205 | +### Reference by Name |
| 206 | + |
| 207 | +```yaml |
| 208 | +# Source PluginPreset - generates a hostname per cluster |
| 209 | +apiVersion: greenhouse.sap/v1alpha1 |
| 210 | +kind: PluginPreset |
| 211 | +metadata: |
| 212 | + name: backend-preset |
| 213 | +spec: |
| 214 | + plugin: |
| 215 | + pluginDefinitionRef: |
| 216 | + name: backend-service |
| 217 | + optionValues: |
| 218 | + - name: backend.hostname |
| 219 | + expression: | |
| 220 | + "backend.${global.greenhouse.clusterName}.example.com" |
| 221 | + clusterSelector: |
| 222 | + matchLabels: |
| 223 | + env: production |
| 224 | +--- |
| 225 | +# Consumer PluginPreset - references the backend hostname |
| 226 | +apiVersion: greenhouse.sap/v1alpha1 |
| 227 | +kind: PluginPreset |
| 228 | +metadata: |
| 229 | + name: frontend-preset |
| 230 | +spec: |
| 231 | + plugin: |
| 232 | + pluginDefinitionRef: |
| 233 | + name: frontend-service |
| 234 | + optionValues: |
| 235 | + - name: frontend.backendUrl |
| 236 | + valueFrom: |
| 237 | + ref: |
| 238 | + kind: PluginPreset |
| 239 | + name: backend-preset |
| 240 | + expression: | |
| 241 | + ${spec.optionValues.filter(v, v.name == "backend.hostname")[0].value} |
| 242 | + clusterSelector: |
| 243 | + matchLabels: |
| 244 | + env: production |
| 245 | +``` |
| 246 | + |
| 247 | +The resulting Plugin will contain the resolved value. For example: |
| 248 | + |
| 249 | +```yaml |
| 250 | +# If the matching cluster is named "production-eu": |
| 251 | +spec: |
| 252 | + optionValues: |
| 253 | + - name: frontend.backendUrl |
| 254 | + value: "backend.production-eu.example.com" |
| 255 | +``` |
| 256 | + |
| 257 | +### Reference by Label Selector |
| 258 | +When multiple PluginPresets need to be referenced, use a label selector. |
| 259 | +The CEL expression is evaluated against each matching PluginPreset and results are collected into an array. |
| 260 | + |
| 261 | + |
| 262 | +```yaml |
| 263 | +# Multiple source PluginPresets with shared label |
| 264 | +apiVersion: greenhouse.sap/v1alpha1 |
| 265 | +kind: PluginPreset |
| 266 | +metadata: |
| 267 | + name: selector-source-a |
| 268 | + namespace: demo |
| 269 | + labels: |
| 270 | + e2e.greenhouse.sap/selector-test: "true" |
| 271 | +spec: |
| 272 | + plugin: |
| 273 | + pluginDefinitionRef: |
| 274 | + name: perses |
| 275 | + releaseName: perses-sel-source-a |
| 276 | + releaseNamespace: kube-monitoring |
| 277 | + optionValues: |
| 278 | + - name: source.endpoint |
| 279 | + expression: | |
| 280 | + "endpoint-a.${global.greenhouse.clusterName}.example.com" |
| 281 | + clusterSelector: |
| 282 | + matchLabels: |
| 283 | + greenhouse.sap/cluster: kind-greenhouse-remote |
| 284 | +--- |
| 285 | +apiVersion: greenhouse.sap/v1alpha1 |
| 286 | +kind: PluginPreset |
| 287 | +metadata: |
| 288 | + name: selector-source-b |
| 289 | + namespace: demo |
| 290 | + labels: |
| 291 | + e2e.greenhouse.sap/selector-test: "true" |
| 292 | +spec: |
| 293 | + plugin: |
| 294 | + pluginDefinitionRef: |
| 295 | + name: perses |
| 296 | + releaseName: perses-sel-source-b |
| 297 | + releaseNamespace: kube-monitoring |
| 298 | + optionValues: |
| 299 | + - name: source.endpoint |
| 300 | + expression: | |
| 301 | + "endpoint-b.${global.greenhouse.clusterName}.example.com" |
| 302 | + clusterSelector: |
| 303 | + matchLabels: |
| 304 | + greenhouse.sap/cluster: kind-greenhouse-remote |
| 305 | +--- |
| 306 | +apiVersion: greenhouse.sap/v1alpha1 |
| 307 | +kind: PluginPreset |
| 308 | +metadata: |
| 309 | + name: selector-consumer |
| 310 | + namespace: demo |
| 311 | +spec: |
| 312 | + plugin: |
| 313 | + pluginDefinitionRef: |
| 314 | + name: perses |
| 315 | + releaseName: perses-sel-consumer |
| 316 | + releaseNamespace: kube-monitoring |
| 317 | + optionValues: |
| 318 | + - name: consumer.endpoints |
| 319 | + valueFrom: |
| 320 | + ref: |
| 321 | + kind: PluginPreset |
| 322 | + selector: |
| 323 | + matchLabels: |
| 324 | + e2e.greenhouse.sap/selector-test: "true" |
| 325 | + expression: | |
| 326 | + ${spec.optionValues.filter(v, v.name == "source.endpoint")[0].value} |
| 327 | + clusterSelector: |
| 328 | + matchLabels: |
| 329 | + greenhouse.sap/cluster: kind-greenhouse-remote |
| 330 | +``` |
| 331 | + |
| 332 | +The consumer Plugin will receive an array of all resolved values: |
| 333 | + |
| 334 | +```yaml |
| 335 | +spec: |
| 336 | + optionValues: |
| 337 | + - name: consumer.endpoints |
| 338 | + value: [ "endpoint-a.kind-greenhouse-remote.example.com", |
| 339 | + "endpoint-b.kind-greenhouse-remote.example.com"] |
| 340 | +``` |
| 341 | + |
| 342 | +### CEL Expression Syntax for References |
| 343 | +The expression field in valueFrom.ref supports multiple syntax styles: |
| 344 | + |
| 345 | +#### New simplified syntax |
| 346 | +```expression: spec.optionValues.filter(v, v.name == "my.value")[0].value``` |
| 347 | + |
| 348 | +#### With ${...} wrapper |
| 349 | +```expression: ${spec.optionValues.filter(v, v.name == "my.value")[0].value}``` |
| 350 | + |
| 351 | +#### Legacy syntax (backward compatible) |
| 352 | +```expression: object.spec.optionValues.filter(v, v.name == "my.value")[0].value``` |
| 353 | + |
| 354 | + |
| 355 | +> :warning: ValueFrom references in PluginPresets only support referencing other PluginPresets (kind: PluginPreset). Referencing standalone Plugins is not supported. |
| 356 | + |
| 357 | +### Feature Flags |
| 358 | +Expression evaluation and ValueFrom.Ref resolution in PluginPresets are controlled by feature flags: |
| 359 | + |
| 360 | +```yaml |
| 361 | +# greenhouse-feature-flags ConfigMap |
| 362 | +pluginPreset: | |
| 363 | + expressionEvaluationEnabled: true |
| 364 | + integrationEnabled: true |
| 365 | +``` |
| 366 | + |
89 | 367 | ## Next Steps |
90 | 368 |
|
91 | 369 | - [Managing Plugins for multiple clusters](./../../../user-guides/plugin/plugin-management) |
92 | 370 | - [Plugin reference](./../plugin) |
93 | | -- [PluginDefinition reference](./../plugindefinition) |
| 371 | +- [PluginDefinition reference](./../plugindefinition) |
0 commit comments