-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathllms.txt
More file actions
501 lines (379 loc) · 22.2 KB
/
llms.txt
File metadata and controls
501 lines (379 loc) · 22.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
# function-starlark
> Crossplane composition function using Starlark (Python-like) scripts. ~20MB memory, hermetic sandbox, built-in dependency ordering, auto-injected labels, OCI module system.
## What this is
A Crossplane composition function that lets you write compositions in Starlark instead of KCL or Go templates. Starlark is a subset of Python -- if you know Python, you know Starlark. Scripts run in a hermetic sandbox (no I/O, no network) with bytecode caching for sub-second execution.
## Installation
```yaml
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-starlark
spec:
package: ghcr.io/wompipomp/function-starlark:latest
```
## StarlarkInput spec fields
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| source | string | -- | Inline Starlark script |
| scriptConfigRef.name | string | -- | ConfigMap name containing the script |
| scriptConfigRef.key | string | main.star | Key within the ConfigMap |
| modules | map[string]string | -- | Inline modules loadable via load() |
| ociDefaultRegistry | string | -- | Default OCI registry for short-form load syntax. Overrides STARLARK_OCI_DEFAULT_REGISTRY env var. |
| ociInsecureRegistries | []string | -- | HTTP-only registries (dev only). Overrides STARLARK_OCI_INSECURE_REGISTRIES env var. |
| dockerConfigSecret | string | -- | Secret name for private OCI registry auth. Overrides STARLARK_DOCKER_CONFIG_SECRET env var. |
| dockerConfigCredential | string | -- | gRPC credential name for private OCI registry auth. Used with crossplane render --function-credentials or Composition credentials block. |
| usageAPIVersion | string | v2 | Crossplane Usage API version: v1 (Crossplane 1.x) or v2 (Crossplane 2.x, default). Overrides STARLARK_USAGE_API_VERSION env var. |
| sequencingTTL | duration | 10s | Response TTL when creation sequencing defers resources |
## Predeclared globals (available in every script)
| Name | Type | Description |
|------|------|-------------|
| oxr | StarlarkDict (read-only) | Observed composite resource. Use get(oxr, "spec.field", default) for safe access. |
| dxr | StarlarkDict (read-write) | Desired composite resource. Write status fields here. |
| observed | StarlarkDict (read-only) | Observed composed resources keyed by composition resource name. |
| context | dict (read-write) | Pipeline context shared across steps. |
| environment | StarlarkDict (read-only) | EnvironmentConfig data. |
| extra_resources | dict (read-only) | Extra resources from require_extra_resource/require_extra_resources. |
## Predeclared functions
### Resource(name, body, ready=None, labels=<auto>, connection_details=None, depends_on=None, external_name=None, when=True, skip_reason="", preserve_observed=False)
Register a desired composed resource. This is the primary function.
- name: Composition resource name (unique string). This replaces KCL's "krm.kcl.dev/composition-resource-name" annotation.
- body: Dict with apiVersion, kind, metadata, spec.
- ready: None (auto-detect), True, or False.
- labels: Omit for auto-injection of crossplane.io/* labels. Dict to merge custom labels. None to opt out.
- connection_details: Dict of string key-value pairs for per-resource connection details.
- depends_on: List of ResourceRef, string, or tuple (ref, field_path) for creation sequencing. Plain refs/strings check existence. Tuples check that the specified field path has a non-empty value in the observed resource. Generates Crossplane Usage resources.
- external_name: Sugar for crossplane.io/external-name annotation.
- when: Bool gate (True/False only). False skips the resource (requires skip_reason). Non-bool values raise type error.
- skip_reason: Human-readable skip reason. Required when when=False without preserve_observed. Appears in Warning event.
- preserve_observed: When True and body is None (or when=False), emit the observed body verbatim if found. Cliff-guard for resources with temporarily unavailable config.
- Returns: ResourceRef with .name attribute for use in depends_on.
Example:
```python
bucket = Resource("bucket", {
"apiVersion": "s3.aws.upbound.io/v1beta1",
"kind": "Bucket",
"metadata": {"name": "%s-data" % xr_name},
"spec": {"forProvider": {"region": region}},
})
Resource("topic", {...}, depends_on=[bucket])
# Wait for a specific field to be populated before creating dependent resource
project = Resource("project", {
"apiVersion": "project.gcp.upbound.io/v1beta1",
"kind": "Project",
"spec": {"forProvider": {"name": project_name}},
})
Resource("group", {...}, depends_on=[
(project, "status.atProvider.projectId"), # wait for field readiness
])
```
### get(obj, path, default=None)
Safe nested dict access with dot-separated path.
- Dot string: get(oxr, "spec.region", "us-east-1")
- List form for keys with dots: get(oxr, ["metadata", "annotations", "app.kubernetes.io/name"], "")
### get_label(res, key, default=None)
Safe label lookup by exact key. Handles dotted keys like "app.kubernetes.io/name" correctly -- unlike get() which splits on dots. Returns default when the key, labels map, or metadata is missing.
```python
name = get_label(oxr, "app.kubernetes.io/name", "unknown")
team = get_label(observed["my-bucket"], "team", "default")
```
### get_annotation(res, key, default=None)
Safe annotation lookup by exact key. Like get_label(), handles dotted annotation keys correctly by going directly to res["metadata"]["annotations"][key]. Returns default when the key, annotations map, or metadata is missing.
```python
ext_name = get_annotation(oxr, "crossplane.io/external-name", "")
zone = get_annotation(observed["my-db"], "topology.kubernetes.io/zone", "")
```
### set_condition(type, status, reason, message)
Set a condition on the XR.
```python
set_condition(type="Ready", status="True", reason="Available", message="All resources created")
```
### emit_event(severity, message)
Emit Normal or Warning event on the XR.
```python
emit_event("Normal", "Reconciliation complete")
emit_event("Warning", "Deprecated API version")
```
### fatal(message)
Halt execution with a fatal error. Sets ReconcileError condition on XR.
```python
if not region:
fatal("spec.region is required")
```
### set_connection_details(details)
Set XR-level connection details.
```python
set_connection_details({"endpoint": "https://...", "password": pw})
```
### set_xr_status(path, value)
Set a status field on the XR at a dot-separated path. Writes to dxr["status"][path segments...], auto-creating intermediate dicts as needed. Uses mkdir-p semantics: non-dict values at intermediate segments are silently overwritten. Path validation rejects empty paths, leading/trailing dots, and consecutive dots. Consecutive writes to the same prefix preserve sibling keys.
```python
# Individual field writes (siblings are preserved)
set_xr_status("atProvider.projectId", project_id)
set_xr_status("atProvider.arn", arn)
set_xr_status("region", region)
# Bulk write for related fields
set_xr_status("atProvider", {"bucketCount": len(buckets), "environment": env})
```
### skip_resource(name, reason)
Remove a resource from desired state (e.g., conditionally skip a resource from a previous pipeline step).
```python
skip_resource("monitoring", "monitoring only in prod")
```
### get_observed(name, path, default=None)
One-call observed resource field lookup. Equivalent to get(observed.get(name, {}), path, default) but in a single call. Returns default when the named resource is missing, the path is missing, or the value is None. Safe on first reconciliation when observed is empty.
```python
bucket_arn = get_observed("my-bucket", "status.atProvider.arn", "")
db_host = get_observed("my-db", "status.atProvider.address", "pending")
```
### require_extra_resource(name, apiVersion, kind, match_name=None, match_labels=None)
Request a single extra resource. Access via extra_resources[name] on next reconciliation.
```python
require_extra_resource("config", "v1", "ConfigMap", match_name="my-config")
```
### require_extra_resources(name, apiVersion, kind, match_labels)
Request multiple extra resources by label selector.
```python
require_extra_resources("certs", "v1", "Secret", match_labels={"type": "tls"})
```
### schema(name, doc=None, **fields)
Define a typed constructor that validates kwargs at construction time. Each kwarg (except doc) must be a field() descriptor. Returns a callable -- calling it validates types, required fields, enum values, and unknown fields, reporting all errors at once. Returns a dict-compatible SchemaDict.
```python
NetworkRules = schema("NetworkRules",
default_action=field(type="string", enum=["Allow", "Deny"]),
ip_rules=field(type="list", items=IpRule),
)
rules = NetworkRules(default_action="Deny")
```
### field(type="", required=False, default=None, enum=None, doc="", items=None)
Define a field descriptor for schema constructors. type= accepts primitive strings ("string", "int", "float", "bool", "list", "dict") or a schema reference for nested validation. Empty type accepts any value (gradual typing). items= is only valid with type="list" and must be a schema.
```python
field(type="string", required=True)
field(type="string", default="Standard", enum=["Standard", "Premium"])
field(type=SubSchema) # nested schema reference
field(type="list", items=SubSchema) # list of schema
```
### struct(**kwargs)
Create an immutable struct with named fields accessible via dot notation. Used internally by namespace alias imports to wrap module exports, but also available directly.
```python
point = struct(x=1, y=2)
point.x # 1
point.y # 2
```
### get_extra_resource(name, path=None, default=None)
One-call extra-resource field lookup. Returns the value at path within the first matching extra resource, or default when the name is missing or path not found. When path is None, returns the full resource body.
```python
region = get_extra_resource("cluster", "spec.region", "us-west-2")
```
### get_extra_resources(name, path=None, default=[])
Get all extra resources for a name as a list. Returns empty list when name not found.
```python
certs = get_extra_resources("tls-secrets")
```
### is_observed(name)
Returns True iff the named composed resource exists in observed state. Use for branching without field access.
```python
if is_observed("database"):
db_host = get_observed("database", "status.atProvider.address", "")
```
### observed_body(name, default=None)
Returns the full observed resource body dict, or default if not found.
```python
db = observed_body("database", default={})
```
### get_condition(name, type)
Returns a condition dict {status, reason, message, lastTransitionTime} for the named condition type on the observed resource, or None. Always returns an unfrozen dict with exactly 4 keys; missing fields default to empty string.
```python
cond = get_condition("database", "Ready")
if cond and cond["status"] == "True":
# database is ready
```
### set_response_ttl(duration)
Set response TTL on RunFunctionResponse. Accepts Go duration string ("30s", "5m") or int seconds. Overrides sequencingTTL.
```python
set_response_ttl("30s")
set_response_ttl(30) # equivalent
```
## Namespace modules
Six predeclared namespace modules. Available in every script without import.
### json (from go.starlark.net/lib/json)
- json.encode(x) -- Starlark value to JSON string
- json.decode(x) -- JSON string to Starlark value
- json.encode_indent(x, prefix="", indent="\t") -- pretty-print JSON
- json.indent(s, prefix="", indent="\t") -- reformat existing JSON string
### crypto
- crypto.sha256(data) -- lowercase hex SHA-256 digest
- crypto.sha512(data) -- lowercase hex SHA-512 digest
- crypto.sha1(data) -- lowercase hex SHA-1 digest
- crypto.md5(data) -- lowercase hex MD5 digest (non-cryptographic use only)
- crypto.hmac_sha256(key, message) -- hex HMAC-SHA256 digest
- crypto.blake3(data) -- lowercase hex BLAKE3 digest
- crypto.stable_id(seed, length=8) -- deterministic hex ID (1-64 chars)
### encoding
- encoding.b64enc(data) / encoding.b64dec(data) -- standard base64
- encoding.b64url_enc(data) / encoding.b64url_dec(data) -- URL-safe base64 (no padding)
- encoding.b32enc(data) / encoding.b32dec(data) -- base32 (no padding)
- encoding.hex_enc(data) / encoding.hex_dec(data) -- hex encoding
### dict
- dict.merge(d1, d2, ...) -- shallow right-wins merge, returns new dict
- dict.deep_merge(d1, d2, ...) -- recursive right-wins merge, returns new dict
- dict.pick(d, keys) -- subset for matching keys
- dict.omit(d, keys) -- copy with keys removed
- dict.compact(d) -- recursively prune None values at any depth. K8s empties (empty string/list/dict) preserved. Max depth 32.
- dict.dig(d, path, default=None) -- dotted-path lookup
- dict.has_path(d, path) -- True iff dotted path exists
### regex (Go RE2 syntax)
- regex.match(pattern, s) -- True if pattern matches anywhere in s
- regex.find(pattern, s) -- first match string or None
- regex.find_all(pattern, s) -- list of all non-overlapping matches
- regex.find_groups(pattern, s) -- list of capture group strings or None
- regex.replace(pattern, s, replacement) -- replace first match ($1 backrefs)
- regex.replace_all(pattern, s, replacement) -- replace all matches
- regex.split(pattern, s) -- split on matches
### yaml (via sigs.k8s.io/yaml)
- yaml.encode(value) -- K8s-compatible YAML string (sorted keys, no trailing newline)
- yaml.decode(s) -- single-doc YAML to Starlark value
- yaml.decode_stream(s) -- multi-doc YAML to list of Starlark values
### conditions.star (stdlib)
- all_ready(resources=None) -- True iff all observed have Ready=True; None+0 observed=False
- any_degraded(resources=None) -- True iff any have Ready=False or Synced=False
## Module system
Load shared code via standard Starlark load():
```python
# Short-form OCI load (recommended, requires default registry configured)
load("function-starlark-stdlib:v1/naming.star", "resource_name")
# Explicit full OCI URL (always works)
load("oci://ghcr.io/wompipomp/starlark-stdlib:v1/naming.star", "resource_name")
# Package-local (inside a published OCI module, load a sibling in the SAME artifact)
load("./sibling.star", "helper") # OCI callers only; flat paths only
# From inline modules defined in StarlarkInput spec.modules
load("helpers.star", "make_tags")
# From ConfigMap-mounted filesystem
load("shared/utils.star", "validate")
```
### Short-form OCI load syntax
Pattern: `load("package:tag/file.star", "name")` -- the colon (`:`) or `@sha256:` in the target triggers default registry expansion.
Detection rules (in order):
1. Starts with `./` and ends with `.star` (flat, no subdirs) -- package-local, expanded against the caller's OCI artifact (OCI callers only)
2. Starts with `oci://` -- explicit full OCI URL (used as-is)
3. Contains `:` or `@sha256:` -- short-form, expanded via default registry
4. Otherwise -- local module (inline or filesystem)
Configuration (spec fields override env vars):
- `spec.ociDefaultRegistry` / `STARLARK_OCI_DEFAULT_REGISTRY` env var
- `spec.ociInsecureRegistries` / `STARLARK_OCI_INSECURE_REGISTRIES` env var (comma-separated)
- `spec.dockerConfigSecret` / `STARLARK_DOCKER_CONFIG_SECRET` env var
- `spec.dockerConfigCredential` (gRPC credential name for private OCI registry auth — used with `crossplane render --function-credentials` locally or Composition `credentials` block in-cluster. Accepts secrets with `config.json` or `.dockerconfigjson` keys. Keychain priority: gRPC credential > filesystem secret > default)
- `spec.usageAPIVersion` / `STARLARK_USAGE_API_VERSION` env var
- `STARLARK_OCI_CACHE_TTL` env var (pod-level only, default 5m)
Expansion example with registry `ghcr.io/wompipomp`:
- `function-starlark-stdlib:v1/naming.star` becomes `oci://ghcr.io/wompipomp/function-starlark-stdlib:v1/naming.star`
Valid short-form patterns:
- `package:tag/file.star` (tag reference)
- `package:tag/subdir/file.star` (nested file path)
- `package@sha256:hex.../file.star` (digest pinning)
### Namespace alias imports
Namespace alias imports wrap all exports in a struct. Solves name conflicts when multiple modules export the same name.
```python
# Namespace import -- all exports wrapped in a struct
load("schemas-k8s:v1.35/apps/v1.star", k8s="*")
load("schemas-azure:v2.5.0/storage/v1.star", storage="*")
load("schemas-azure:v2.5.0/cosmosdb/v1.star", cosmosdb="*")
# Access via namespace dot notation
k8s.Deployment(...)
storage.Account(...) # no conflict with cosmosdb.Account
cosmosdb.Account(...)
# Mix named and namespace imports
load("module.star", "SpecificFunc", ns="*")
# SpecificFunc available flat, everything else via ns.Name
```
Backwards compatible -- plain `load("mod", "*")` still works for flat imports.
## Standard library (oci://ghcr.io/wompipomp/starlark-stdlib:v1/)
### naming.star
- resource_name(suffix, xr_name=None): Generate K8s-safe name from XR name + suffix, auto-truncates to 63 chars with hash.
- hash_suffix(name, length=5): Deterministic base-36 hash suffix.
### networking.star
- cidr_subnet(base, newbits, netnum): Calculate subnet CIDR from base network.
- ip_add(ip, offset): Add offset to IP address.
### labels.star
- standard_labels(oxr, extra={}): Generate standard labels from XR metadata.
### conditions.star
- ready(oxr): Check if XR has all conditions True.
- degraded(oxr, message): Set degraded condition.
## Composition pattern: Extract-Transform-Emit
```python
# 1. Extract
region = get(oxr, "spec.region", "us-east-1")
env = get(oxr, "spec.environment", "dev")
# 2. Transform
bucket_config = {
"apiVersion": "s3.aws.upbound.io/v1beta1",
"kind": "Bucket",
"spec": {"forProvider": {"region": region}},
}
# 3. Emit
Resource("bucket", bucket_config)
```
## Best practices
- Use get() for all nested access -- never raw bracket chains on XR/observed data.
- Use get_label()/get_annotation() instead of get() for dotted label/annotation keys -- get() splits on dots, so "app.kubernetes.io/name" won't work as a dot-path.
- Use get_observed() for one-call observed resource field access instead of manual "if name in observed" existence checks.
- Use resource_name(suffix) from stdlib for metadata.name to handle 63-char K8s limit.
- Use external_name= kwarg instead of manual crossplane.io/external-name annotation.
- Use labels= kwarg for custom labels. Let auto-injection handle crossplane.io/* labels.
- Use fatal() for validation errors (not fail()).
- Use emit_event() for observability.
- Use depends_on= for creation ordering between resources.
- Use set_xr_status() for writing XR status fields -- supports dot-paths with auto-created intermediates and sibling preservation.
- Use %s string formatting (Starlark convention, .format() also works).
- Put helper functions at the top of the script.
- End scripts with emit_event("Normal", "Reconciled ...") for observability.
- Use namespace alias imports (alias="*") when loading provider schema packages to avoid name conflicts across API groups.
- Use get_extra_resource() for one-call extra-resource field access instead of manual extra_resources dict navigation.
- Use is_observed()/observed_body() for existence checks and body retrieval instead of manual "if name in observed" patterns.
- Use get_condition() for reading observed resource conditions instead of navigating status.conditions arrays.
- Use set_response_ttl() for custom requeue intervals instead of relying on sequencingTTL.
- Use namespace modules (json, crypto, encoding, dict, regex, yaml) directly -- no import needed.
## Schema validation
Opt-in typed constructors that catch typos, wrong types, and missing fields at construction time. Schemas are defined inline alongside your composition logic -- no separate schema files or imports needed.
```python
# Define schemas (flat, nested, and list-of-schema)
IpRule = schema("IpRule",
action=field(type="string", default="Allow"),
ip_address=field(type="string", required=True),
)
Account = schema("Account",
location=field(type="string", required=True),
sku=field(type="string", default="Standard_LRS", enum=["Standard_LRS", "Standard_GRS"]),
network_rules=field(type=IpRule), # nested schema
ip_rules=field(type="list", items=IpRule), # list of schema
)
# Use -- returns a dict-compatible SchemaDict that works with Resource()
acct = Account(location="eastus", ip_rules=[{"ip_address": "10.0.0.1"}])
Resource("storage", {"apiVersion": "...", "kind": "Account", "spec": {"forProvider": acct}})
```
Schema constructors return dict-compatible values that work transparently with Resource(). You can mix schema-validated and plain dict resources in the same composition for incremental adoption.
## KCL to Starlark migration cheat sheet
| KCL | Starlark |
|-----|----------|
| option("params")?.oxr | oxr (predeclared global) |
| option("params")?.ocds | observed (predeclared global) |
| oxr?.spec?.field | get(oxr, "spec.field", default) |
| "krm.kcl.dev/composition-resource-name" annotation | First argument to Resource() |
| items = [resource1, resource2] | Resource("name1", resource1); Resource("name2", resource2) |
| dxr = {**oxr, status.field = val} | set_xr_status("field", val) |
| "crossplane.io/external-name" annotation | external_name= kwarg on Resource() |
| assert condition, message | if not condition: fatal(message) |
| import module | load("module.star", "func") |
## Ecosystem
- **function-starlark-gen** (github.com/wompipomp/function-starlark-gen): Go CLI code generator. Generates typed Starlark schema libraries from OpenAPI specs, K8s CRDs, and Crossplane provider CRDs. Three modes: `starlark-gen k8s`, `starlark-gen crd`, `starlark-gen provider`. Generated schemas use schema() and field() builtins.
- **function-starlark-schemas** (github.com/wompipomp/function-starlark-schemas): Pre-built schema packages published as OCI artifacts at ghcr.io/wompipomp/schemas-*. Load via `load("schemas-k8s:v1.35/apps/v1.star", k8s="*")`.
- **function-starlark-vscode** (github.com/wompipomp/function-starlark-vscode): VS Code extension. Autocomplete for all builtins, hover docs, signature help, syntax highlighting, format-on-save via buildifier, schema IntelliSense with OCI-based schema auto-download.
## Starlark language notes
Starlark is Python-like but NOT Python:
- No classes, exceptions, try/except, import, with, yield, async/await
- No set type (use dict with None values)
- No **kwargs or *args
- Strings are immutable, no f-strings (use % or .format())
- Variables assigned in if/for are visible in outer scope
- Top-level code executes sequentially (no main function)
- Integers have arbitrary precision (no overflow)
- for/while loops have a step limit (prevents infinite loops)