Skip to content

Commit 7878928

Browse files
committed
feature: Add Documentation for CueX usage and examples
Signed-off-by: Brian Kane <briankane1@gmail.com>
1 parent 52a72ac commit 7878928

File tree

3 files changed

+285
-0
lines changed

3 files changed

+285
-0
lines changed
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
---
2+
title: External CUE Packages
3+
---
4+
5+
# Extending CUE with External Packages
6+
7+
CueX extends the CUE templating system by allowing users to define and execute custom functions as external packages. This enables integration with external services, dynamic function execution and reusability within workflow steps.
8+
9+
With CueX external packages, you can:
10+
- Define reusable functions for workflow steps.
11+
- Dynamically fetch data from APIs, databases, or external services.
12+
- Avoid redundant logic by centralizing function definitions into reusable packages.
13+
- Implement function providers in any language, as long as they expose endpoints that conform to CueX requirements.
14+
15+
CueX introduces the `Package` CustomResource, which acts as a registry for reusable functions. A `Package` consists of:
16+
17+
> **Note:** External packages are available for use only within `WorkflowStepDefinitions`.
18+
19+
---
20+
21+
## Structure of a Package
22+
A `Package` in CueX is a Kubernetes CustomResource (CRD) that defines an external function provider and the functions it exposes. The key attributes of `Packages` are:
23+
24+
#### metadata.name
25+
- The name of the `Package`, used to reference it in the `#provider` field.
26+
- This must match the `#provider` value within function templates.
27+
28+
#### spec.path
29+
- Defines the import path of the package within CUE definitions.
30+
- In the below example, `ext/example` means functions from this package can be referenced as:
31+
32+
```cue
33+
import "ext/utils"
34+
35+
result: utils.#ExampleFunctionName & {
36+
$params: { example_parameter: "value" }
37+
}
38+
```
39+
40+
#### spec.provider
41+
Specifies where CueX should send function calls.
42+
- `spec.provider.protocol`: Defines the communication method (currently only `"http"`).
43+
- `spec.provider.endpoint`: The root endpoint of the function provider (e.g. http://my-cuex-server:8443/api/v1)
44+
> e.g. If `#do: "api/example-fn-name"` is defined in a function, CueX will send `POST {provider_endpoint}/api/example-fn-name`.
45+
46+
#### spec.templates
47+
- Defines CUE function templates that describe available functions.
48+
- Each function includes:
49+
- `#do`: The function path that CueX will call (appended to `spec.provider.endpoint` at execution time).
50+
- `#provider`: The name of the package (must match `metadata.name`).
51+
- `$params`: The expected input format.
52+
- `$returns`: The expected output format.
53+
54+
### Example
55+
```
56+
apiVersion: cue.oam.dev/v1alpha1
57+
kind: Package
58+
metadata:
59+
name: external-package # Unique package name
60+
spec:
61+
path: ext/example # Import path for CueX to reference
62+
provider:
63+
protocol: http # Currently, only `http` is supported
64+
endpoint: http://my-cuex-server:8443 # Root endpoint of the provider service
65+
templates:
66+
utils.cue: | # The filename inside the package
67+
package example // Recommended to match the final segment of the {spec.path} value
68+
69+
#ExampleFunctionName: { // Available in Cue as example.#ExampleFunctionName
70+
#do: "api/example-fn-name" // CueX will call `{spec.provider.endpoint}/api/example-fn-name`
71+
#provider: "external-package" // Must match metadata.name
72+
$params: {
73+
example_param: string // Structure should match the parameters of the Fn
74+
}
75+
$returns: {
76+
example_result: number // Structure should match the return structure of the Fn
77+
}
78+
}
79+
```
80+
81+
---
82+
83+
## How CueX Resolves Calls
84+
For the above package, when CueX encounters:
85+
```cue
86+
import "ext/example"
87+
88+
result: example.#ExampleFunctionName & {
89+
$params: { example_param: "A string value.." }
90+
}
91+
```
92+
93+
It will:
94+
1. Look up the `#provider` (_"external-package"_).
95+
2. Identify that `#do: "api/example-fn-name"` maps to the provider’s POST _{endpoint}/api/example-fn-name_.
96+
3. Send a POST _http://my-cuex-server:8443/api/example-fn-name_ request with this payload:
97+
```json
98+
{ "example_param": "A string value.." }
99+
```
100+
4. Expect a response like:
101+
```json
102+
{ "example_result": "A returned string value.."}
103+
```
104+
5. Populate `$returns` in the CUE template with the received result.
105+
106+
---
107+
108+
## Implementing a Provider
109+
110+
External function providers can be written in any programming language, as long as they expose HTTP POST endpoints that match CueX’s expected request/response format.
111+
112+
### Provider Requirements
113+
1. **Expose an HTTP API with matching paths**: Each function must be available at an HTTP POST endpoint, and the value specified in **#do** within the CUE Package must match the request path on the provider’s service.
114+
115+
For example, if `#do: "example"` is specified in the CUE package, the provider must expose a corresponding `/example` HTTP endpoint that handles the request.
116+
117+
2. **Accept JSON input**: The request payload should be a JSON object with a structure matching `$params`.
118+
119+
3. **Return JSON output**: The response should be a JSON object with a structure matching `$returns`.
120+
121+
![cuex-usage.png](../../resources/cuex-usage.png)
122+
123+
---
124+
125+
## Example: Simple CUE Provider & Package
126+
Lets define a very simple function `"sum"` which accepts two inputs and returns the sum.
127+
The function definition will look this:
128+
```cue
129+
#Sum: {
130+
#do: "sum"
131+
#provider: "external-cuex-package"
132+
$params: {
133+
x: number
134+
y: number
135+
}
136+
$returns: {
137+
result: number
138+
}
139+
}
140+
```
141+
142+
### Implementing the Provider
143+
#### Implementing the Provider in Go
144+
Here’s an example of a very simple external CueX Provider written as a Go service.
145+
146+
It implements the logic for the `"sum"` function and exposes it at `/sum`.
147+
148+
```go
149+
package main
150+
151+
import (
152+
"encoding/json"
153+
"net/http"
154+
)
155+
156+
type input struct {
157+
X int `json:"x"` // Matches the $params structure
158+
Y int `json:"y"`
159+
}
160+
161+
type output struct {
162+
Result int `json:"result"` // Matches the $returns structure
163+
}
164+
165+
func sumHandler(w http.ResponseWriter, r *http.Request) {
166+
var in input
167+
json.NewDecoder(r.Body).Decode(&in)
168+
169+
out := output{Result: in.X + in.Y}
170+
w.Header().Set("Content-Type", "application/json")
171+
json.NewEncoder(w).Encode(out)
172+
}
173+
174+
func main() {
175+
http.HandleFunc("/sum", sumHandler) // Path must match `#do: "sum"`
176+
http.ListenAndServe(":8443", nil)
177+
}
178+
```
179+
180+
> **Note**: KubeVela provides a [simple server package](https://github.com/kubevela/pkg/blob/main/cue/cuex/externalserver/server.go) that can be utilised.
181+
182+
#### Implementing the Provider in Python
183+
For comparison, here’s the same CueX Provider implemented as a simple Python service:
184+
185+
```python
186+
from flask import Flask, request, jsonify
187+
188+
app = Flask(__name__)
189+
190+
@app.route("/sum", methods=["POST"]) # Path must match `#do: "sum"`
191+
def sum_numbers():
192+
data = request.json
193+
x = data.get("x", 0) # Extract values specified in $params
194+
y = data.get("y", 0)
195+
return jsonify({"result": x + y}) # Return JSON response as structure matching $returns
196+
197+
if __name__ == "__main__":
198+
app.run(host="0.0.0.0", port=8443)
199+
```
200+
201+
### Registering the Provider as a Package
202+
Once the provider is running, it can be registered in Kubernetes as a `Package`:
203+
204+
```
205+
apiVersion: cue.oam.dev/v1alpha1
206+
kind: Package
207+
metadata:
208+
name: external-cuex-package
209+
spec:
210+
path: ext/utils # Import path for CueX to reference
211+
provider:
212+
protocol: http
213+
endpoint: http://my-cuex-server:8443 # URL of the running provider service
214+
templates:
215+
utils.cue: |
216+
package utils
217+
218+
#Sum: { // Function name exposed to templates
219+
#do: "sum" // Function path to the provider services endpoint
220+
#provider: "external-cuex-package" // The function provider (should match `metadata.name`)
221+
$params: {
222+
x: number
223+
y: number
224+
}
225+
$returns: {
226+
result: number
227+
}
228+
}
229+
230+
```
231+
232+
### Using the Package
233+
With the Package registered, the `utils.#Sum` function is now available for use with WorkflowStepDefinition templates by importing the `ext/utils` provider.
234+
235+
```cue
236+
ˇimport (
237+
"ext/utils" // The external provider
238+
"vela/op"
239+
)
240+
241+
"example-workflow-step": {
242+
type: "workflow-step"
243+
description: "Workflow Step using external package"
244+
}
245+
246+
template: {
247+
parameter: {}
248+
249+
sum: utils.#Sum & { // The exposed package & function
250+
$params: {
251+
"x": 2,
252+
"y": 5
253+
}
254+
} @step(1)
255+
256+
msg: op.#Message & {
257+
message: "The result is \(sum.$returns.result)" // sum.$returns = { "result": 7 }
258+
} @step(2)
259+
}
260+
```
261+
262+
---
263+
264+
## Configuration Flags
265+
The following KubeVela flags control the behavior of external Package handling in CueX:
266+
267+
| Flag | Type | Default | Description |
268+
|-------------------------------------------------------|------|----------|-------------------------------------------------------------------------------|
269+
| `enable-external-package-for-default-compiler` | bool | `true` | Enables the use of external packages. |
270+
| `enable-external-package-watch-for-default-compiler` | bool | `false` | Allows automatic reloading of external packages without restarting KubeVela. |
271+
272+
---
273+
274+
## Summary
275+
276+
CueX external packages provide a **powerful, reusable, and modular** way to extend workflow templating in KubeVela. By defining external functions as Kubernetes resources, you can:
277+
278+
- Extend CueX with custom logic, like API calls or database queries.
279+
- Reuse functions across multiple workflow steps, reducing duplication.
280+
- Dynamically update functions without modifying individual workflow definitions.
281+
- Maintain consistency across teams by centralizing function logic.
282+
- Implement function providers in any language, as long as they expose HTTP endpoints matching CueX’s requirements.
283+
284+
With external packages, CueX makes it easier to build scalable, maintainable, and extensible workflow steps in KubeVela.

docs/resources/cuex-usage.png

891 KB
Loading

sidebars.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ module.exports = {
321321
},
322322
'platform-engineers/system-operation/velaql',
323323
'platform-engineers/x-def-version',
324+
'platform-engineers/cue/external-packages'
324325
],
325326
},
326327
{

0 commit comments

Comments
 (0)