You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: document Trait removes and toEndpointResources macro
- Add a "Removing Resources" section to the Patching Syntax page covering
the Trait spec.removes section: target shape, execution order, the
workload-kind removal restriction, and forEach removal.
- Document the opt-in workload.toEndpointResources() CEL macro under
Workload Helpers, with route fields and gRPC/HTTP examples, and link to
it from the workload context-variables reference.
Signed-off-by: Chathuranga Dassanayake <chathura2ihl@gmail.com>
Copy file name to clipboardExpand all lines: docs/platform-engineer-guide/component-types/patching-syntax.md
+69Lines changed: 69 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,6 +15,8 @@ Traits can modify existing resources using patches, which are JSON Patch operati
15
15
- CEL-based resource targeting
16
16
- forEach iteration support
17
17
18
+
A Trait can also **delete** whole resources produced by the ComponentType or earlier traits using the `spec.removes` section. See [Removing Resources](#removing-resources).
19
+
18
20
## Basic Patch Structure
19
21
20
22
Patches are defined in the Trait's `spec.patches` section:
@@ -272,6 +274,73 @@ patches:
272
274
name: ${port.name}
273
275
```
274
276
277
+
## Removing Resources
278
+
279
+
Patches modify resources in place. When a Trait needs to **delete** a whole resource produced by the ComponentType or by an earlier trait, use the `spec.removes` section instead. Each entry matches resources by GVK (with an optional `where` filter) and removes the matched resources entirely from the rendered output.
280
+
281
+
```yaml
282
+
apiVersion: openchoreo.dev/v1alpha1
283
+
kind: Trait
284
+
metadata:
285
+
name: drop-default-route
286
+
spec:
287
+
removes:
288
+
- target:
289
+
kind: HTTPRoute
290
+
group: gateway.networking.k8s.io
291
+
version: v1
292
+
targetPlane: dataplane
293
+
```
294
+
295
+
A remove entry uses the same `target` shape as a patch, but has no `operations` — the whole matched resource is deleted:
| `target.group` | Yes | API group; use `""` for core API resources |
301
+
| `target.version` | Yes | API version (e.g. `v1`) |
302
+
| `target.where` | No | CEL expression filtering which matching resources are removed |
303
+
| `targetPlane` | No | Plane whose resources are targeted; defaults to `"dataplane"` |
304
+
| `forEach` / `var` | No | Iterate over a CEL list, binding each item to `var` for use in `where` |
305
+
306
+
**Execution order** - Within a single trait, removes run **after** its `creates` and `patches`. This lets one trait fully express a substitution: create a replacement resource and then remove the original.
307
+
308
+
```yaml
309
+
spec:
310
+
creates:
311
+
- template:
312
+
apiVersion: v1
313
+
kind: ConfigMap
314
+
metadata:
315
+
name: ${metadata.name}-tuned-config
316
+
data:
317
+
mode: optimized
318
+
removes:
319
+
# Drop the ConfigMap the ComponentType emitted, now that the tuned one exists
The primary workload is defined by the ComponentType, so traits **must not** delete it. The admission webhook rejects removes that target a built-in workload GVK — kinds `Deployment`, `StatefulSet`, `DaemonSet`, `CronJob`, or `Job` in the `apps` or `batch` groups. The match is on the full GVK, so a custom CRD that merely shares one of these kind names in a different group (e.g. `group: example.com`, `kind: Deployment`) is **not** rejected.
329
+
:::
330
+
331
+
`forEach` is supported just like in patches, letting you remove a set of resources derived from a CEL list:
Copy file name to clipboardExpand all lines: docs/reference/cel/context-variables.md
+7Lines changed: 7 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -192,6 +192,13 @@ containers:
192
192
port: ${workload.endpoints[endpoint].port}
193
193
```
194
194
195
+
**Helper methods:** The `workload` object exposes two endpoint helpers:
196
+
197
+
- `workload.toServicePorts()`— converts the endpoints map into Kubernetes Service ports.
198
+
- `workload.toEndpointResources(endpointName)`— opt-in; parses the named endpoint's `schema` (OpenAPI for HTTP, protobuf for gRPC) into a CEL optional wrapping a list of `{kind, service, method, path}` routes, for rendering exact per-route gateway matches. Consume it with `.orValue([])` or `.hasValue()`/`.value()`.
199
+
200
+
See [Helper Functions — Workload Helpers](./helper-functions.md#workload-helpers) for details.
201
+
195
202
### configurations
196
203
197
204
Configuration and secret references extracted from the workload container.
Copy file name to clipboardExpand all lines: docs/reference/cel/helper-functions.md
+67Lines changed: 67 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -603,6 +603,73 @@ spec:
603
603
- **BasePath usage**: For HTTPRoute path rewriting, use `workload.endpoints[endpointName].basePath` to configure URL path prefixes
604
604
- **TargetPort distinction**: `targetPort` (container listening port) vs `port` (service port) - the helper uses the correct values for each
605
605
606
+
### workload.toEndpointResources(endpointName)
607
+
608
+
Parses a single endpoint's API schema (the OpenAPI document for HTTP endpoints, the protobuf definition for gRPC endpoints) and returns the flat list of routes it declares. This lets a ComponentType render **exact** per-route gateway matches — one match per OpenAPI path/method, or per gRPC service/method — instead of a single catch-all rule.
609
+
610
+
This helper is **opt-in**: endpoint schemas are only parsed when a template actually calls `workload.toEndpointResources(...)`, so templates that don't use it pay no parsing cost.
| `endpointName` | string | The endpoint map key (e.g. `"http"`, `"grpc"`) — the key used in `workload.endpoints` |
617
+
618
+
**Returns:** a CEL **optional** wrapping a list of route objects. Consume it with `.orValue([])`, or guard with `.hasValue()` / `.value()`. Each route object contains:
Routes are returned in a deterministic (sorted) order so rendered output is stable.
628
+
629
+
:::note Best-effort extraction
630
+
A missing, empty, or unparseable schema (or an endpoint protocol with no extractor, such as TCP/UDP) yields an empty list — never a render failure. Schema format is inferred from the endpoint type (`HTTP` → OpenAPI, `gRPC` → protobuf) unless `workload.endpoints[name].schema.type` overrides it. Only OpenAPI and protobuf extractors exist today; GraphQL and AsyncAPI are reserved and currently degrade to an empty list.
631
+
:::
632
+
633
+
**Examples:**
634
+
635
+
```yaml
636
+
# gRPC: render one GRPCRoute match per (service, method) from the proto schema.
637
+
# Falls back to a catch-all rule (no matches) when the endpoint has no parseable schema.
- Returns a CEL optional, not a plain list — always unwrap with `.orValue([])` or `.hasValue()`/`.value()`.
670
+
- Reads from `workload.endpoints[endpointName].schema`; an endpoint with no schema yields an empty list.
671
+
- Complete, runnable ComponentType examples ship in the OpenChoreo repo under `samples/component-types/component-grpc-service/` and `samples/component-types/component-http-openapi-service/`.
0 commit comments