Skip to main content
Version: v1.11-alpha

Resource Builder

Resource builders are the fluent API for constructing Kubernetes resource manifests in Go. They produce the CUE that becomes the output / outputs blocks of a definition template. Path syntax supports dot-notation (spec.replicas), array indices (containers[0]), and bracket notation (labels[app.oam.dev/name]).

Constructors

ConstructorDescription
defkit.NewResource(apiVersion, kind string) *ResourceCreates a typed Kubernetes resource builder. Chain .Set(), .SetIf(), .If()/.EndIf(), and .SpreadIf() to populate fields. Pass the result to tpl.Output() or tpl.Outputs(name, r).
defkit.NewResourceWithConditionalVersion(kind string) *ResourceCreates a resource whose apiVersion is determined at runtime. Chain .VersionIf(condition, apiVersion) clauses; the first matching condition wins. Use for K8s version differences (e.g. autoscaling/v2beta2 vs autoscaling/v2).
defkit.FromTyped(obj runtime.Object) (*Resource, error) / defkit.MustFromTyped(obj) *ResourceConvert a Go Kubernetes object into a *Resource for further chaining.

Resource Methods

MethodDescription
.Set(path, value)Sets a field at the given path. Supports dot notation ("metadata.name"), array indexing ("spec.containers[0].image"), and bracket notation ("metadata.labels[app.oam.dev/name]").
.SetIf(cond, path, value)Conditionally sets a field only when the condition is true. Generates if cond { path: value }.
.SpreadIf(cond, path, value)Conditionally merges a value into a path. The condition is placed inside the target struct. Use for merging user-provided maps.
.If(cond) / .EndIf()Opens/closes a conditional block. All .Set() calls between are wrapped in a single if cond { ... }. Use when several fields share a guard.
.Directive(path, directive)Adds a CUE directive comment at the given path (e.g. Directive("spec.containers", "patchKey=name")).
.VersionIf(cond, apiVersion)Sets the API version conditionally. Used with NewResourceWithConditionalVersion() for K8s version differences.
.ConditionalStruct(cond, path, fn)Emits an entire named sub-struct only when the condition is true. The closure receives an *OutputStructBuilder exposing Set() and SetIf().
caution

Use .ConditionalStruct() instead of .If()/.EndIf() when the entire struct should be absent from the output when the condition is false. .If()/.EndIf() guards individual fields but still emits the parent struct as an empty {}.

ArrayBuilder Methods

defkit.NewArray() returns an *ArrayBuilder that produces a CUE list literal. Mix static, conditional, and comprehension-based entries in a single array.

MethodDescription
.Item(elem) / .ItemIf(cond, elem)Adds a static / conditional element to the array.
.ForEach(source, elem)For-comprehension over a source collection.
.ForEachGuarded(guard, source, elem)Guarded comprehension — skips when guard is false.
.ForEachWith(source, fn func(item *ItemBuilder))Callback iteration with access to item-builder methods.
.ForEachWithVar(varName, source, fn)Named variable iteration — access iteration variable by name.
.ForEachWithGuardedFiltered(guard, filter, source, fn)Full combo: guarded + filtered comprehension.
.ForEachWithGuardedFilteredVar(varName, guard, filter, source, fn)Full combo with named variable.

Element & Concat Helpers

HelperDescription
defkit.NewArrayElement()Builds one element object. Methods: .Set(key, value), .SetIf(cond, key, value), .PatchKeyField(field, key, value).
defkit.ArrayConcat(left, right Value)Concatenates two list values using CUE's + operator (e.g. a builder-emitted list with a parameter array reference).

ItemBuilder (ForEachWith callback)

The closure passed to .ForEachWith*() receives an *ItemBuilder with these methods: Var() (access iteration variable via .Ref(), .Field(name)), Set(field, value), If(cond, fn), IfSet(field, fn), IfNotSet(field, fn), Let(name, value), SetDefault(field, defValue, typeName), FieldExists(field), FieldNotExists(field). See ForEachWith / ItemBuilder for the per-element builder pattern.

List Comprehensions

Three ways to build CUE list comprehensions from array parameters or let-bound arrays.

HelperDescription
defkit.Each(param).Map(FieldMap)Iterates a parameter and maps each item using the given field map.
defkit.From(param)Supports chained transformations: .Filter(cond), .Map(FieldMap), .Dedupe(field), .Guard(cond).
defkit.ForEachIn(letVar).MapFields(FieldMap)Iterates a LetVariable (for let-bound arrays).

Field Helpers

HelperDescription
defkit.FieldRef(name)References the named field from the current iteration element.
defkit.FieldMap{...}A map[string]Value specifying field-to-value mappings for comprehension output.
defkit.FieldEquals(field, value)Produces a filter predicate for defkit.From().Filter().

Helper Builder

See Helper Builder for tpl.Helper(), tpl.AddLetBinding(), and related let-variable APIs.

Example

Let's build a scalable-service component — a Deployment paired with a HorizontalPodAutoscaler whose apiVersion adapts to the cluster's Kubernetes version, an optional Service for exposed ports, and an optional ServiceAccount. Users supply an image, replica counts, optional CPU/memory requests, optional labels and annotations, a list of pod volumes, optional extra volume mounts and custom metrics, optional environment variables, ports, tolerations, an affinity toggle, and a service account name.

Behind the scenes the template exercises every API listed above in a single definition: NewResource plus Set / SetIf / SpreadIf / If/EndIf / Directive / ConditionalStruct for the Deployment; NewResourceWithConditionalVersion plus VersionIf for the version-aware HPA; MustFromTyped to seed the optional ServiceAccount from a Go type; NewArray mixing Item, ItemIf, ForEach, ForEachGuarded, ForEachWith, ForEachWithVar, and ForEachWithGuardedFiltered (the *Var variants take a leading iteration-variable name and are otherwise identical); NewArrayElement for nested element bodies; defkit.Each(...).Map(...) and defkit.ForEachIn(...).MapFields(...) for list comprehensions; defkit.From(...) chained with .Filter, .Map, .Dedupe, .Guard; FieldRef, FieldMap, and FieldEquals for field references and predicates; and ArrayConcat to splice user-supplied extras onto a derived list. Building on the my-platform module scaffolded in Quick Start, drop the file below into my-platform/components/.

package components

import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/oam-dev/kubevela/pkg/definition/defkit"
)

func ScalableService() *defkit.ComponentDefinition {
image := defkit.String("image").Description("Container image")
replicas := defkit.Int("replicas").Default(1)
cpu := defkit.String("cpu").Optional()
memory := defkit.String("memory").Optional()
labels := defkit.StringKeyMap("labels").Optional()
annotations := defkit.StringKeyMap("annotations").Optional()
volumes := defkit.List("volumes").Description("Pod volumes")
extraMounts := defkit.Array("extraVolumeMounts").Description("Additional container volumeMounts appended via ArrayConcat — required so the generated `[...] + parameter.extraVolumeMounts` is always well-formed; pass `[]` when none are needed")
customMetrics := defkit.Array("customMetrics").Optional()
minReplicas := defkit.Int("minReplicas").Default(1)
maxReplicas := defkit.Int("maxReplicas").Default(10)
env := defkit.Array("env").Optional()
ports := defkit.Array("ports").Optional()
tolerations := defkit.Array("tolerations").Optional()
requireAffinity := defkit.Bool("requireAffinity").Default(false)
serviceAccountName := defkit.String("serviceAccountName").Optional()

return defkit.NewComponent("scalable-service").
Description("Deployment paired with a version-aware HPA").
Workload("apps/v1", "Deployment").
Params(image, replicas, cpu, memory, labels, annotations,
volumes, extraMounts, customMetrics, minReplicas, maxReplicas,
env, ports, tolerations, requireAffinity, serviceAccountName).
Template(scalableServiceTemplate)
}

func scalableServiceTemplate(tpl *defkit.Template) {
vela := defkit.VelaCtx()
image := defkit.String("image")
replicas := defkit.Int("replicas")
cpu := defkit.String("cpu")
memory := defkit.String("memory")
labels := defkit.StringKeyMap("labels")
annotations := defkit.StringKeyMap("annotations")
volumes := defkit.List("volumes")
customMetrics := defkit.Array("customMetrics")
minReplicas := defkit.Int("minReplicas")
maxReplicas := defkit.Int("maxReplicas")
env := defkit.Array("env")
ports := defkit.Array("ports")
tolerations := defkit.Array("tolerations")
requireAffinity := defkit.Bool("requireAffinity")
serviceAccountName := defkit.String("serviceAccountName")

// ServiceAccount built from a typed Go object — MustFromTyped.
sa := defkit.MustFromTyped(&corev1.ServiceAccount{
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "ServiceAccount"},
}).
Set("metadata.name", serviceAccountName)
tpl.OutputsIf(serviceAccountName.IsSet(), "serviceaccount", sa)

// Pod-level volumes — Each + Map + FieldMap + FieldRef + NestedFieldMap.
podVolumes := defkit.Each(volumes).Map(defkit.FieldMap{
"name": defkit.FieldRef("name"),
"emptyDir": defkit.NestedFieldMap(defkit.FieldMap{}),
})

// Container volumeMounts — ArrayBuilder + ForEach (basic, source is required)
// + NewArrayElement, then ArrayConcat to splice user-supplied extras.
mountsArr := defkit.NewArray().ForEach(volumes, defkit.NewArrayElement().
Set("name", defkit.Reference("m.name")).
Set("mountPath", defkit.Reference("m.mountPath")))
allMounts := defkit.ArrayConcat(mountsArr,
defkit.ParamRef("extraVolumeMounts"))

// Container env list — ForEachIn (top-level list-comprehension helper).
containerEnv := defkit.ForEachIn(env).MapFields(defkit.FieldMap{
"name": defkit.FieldRef("name"),
"value": defkit.FieldRef("value"),
})

// envFrom secret refs — From + Filter + Map + FieldEquals. Picks env
// entries with required:true; the SetIf below already gates on env
// presence, so a separate Guard() is unnecessary.
envFromList := defkit.From(env).
Filter(defkit.FieldEquals("required", true)).
Map(defkit.FieldMap{
"secretRef": defkit.NestedFieldMap(defkit.FieldMap{
"name": defkit.FieldRef("name"),
}),
})

// Demo of From().Dedupe().Guard() — kept here as a reference for the
// dedupe + guard pipeline, which produces:
// if parameter.tolerations != _|_ {
// [for v in parameter.tolerations if !already-seen-by-key { v }]
// }
_ = defkit.From(tolerations).Dedupe("key").Guard(tolerations.IsSet())

// Container ports — ForEachWith with the default "v" iteration variable
// + ItemBuilder.IfSet for the optional port name.
containerPorts := defkit.NewArray().ForEachWith(ports, func(item *defkit.ItemBuilder) {
v := item.Var()
item.Set("containerPort", v.Field("port"))
item.IfSet("name", func() { item.Set("name", v.Field("name")) })
})

// Pod tolerations — ForEachWithVar with named "t" iteration variable.
tolerationsArr := defkit.NewArray().ForEachWithVar("t", tolerations, func(item *defkit.ItemBuilder) {
t := item.Var()
item.Set("key", t.Field("key"))
item.Set("effect", t.Field("effect"))
item.Set("operator", defkit.Lit("Exists"))
})

// Service ports — ForEachWithGuardedFiltered (default "v") for ports.expose==true.
// ForEachWithGuardedFilteredVar takes the same arguments plus a leading
// varName to rename the iteration variable; pair it with var-aware
// predicates when you need a non-default name.
servicePorts := defkit.NewArray().ForEachWithGuardedFiltered(
ports.IsSet(),
defkit.FieldEquals("expose", true),
ports,
func(item *defkit.ItemBuilder) {
v := item.Var()
item.Set("port", v.Field("port"))
item.Set("targetPort", v.Field("port"))
item.IfSet("name", func() { item.Set("name", v.Field("name")) })
})

// Deployment — Set / SetIf / SpreadIf / If-EndIf / Directive / ConditionalStruct.
dep := defkit.NewResource("apps/v1", "Deployment").
Set("metadata.name", vela.Name()).
SetIf(annotations.IsSet(), "metadata.annotations", annotations).
SpreadIf(labels.IsSet(), "metadata.labels", labels).
Set("spec.replicas", replicas).
Set("spec.selector.matchLabels[app.oam.dev/component]", vela.Name()).
Set("spec.template.metadata.labels[app.oam.dev/component]", vela.Name()).
SetIf(serviceAccountName.IsSet(), "spec.template.spec.serviceAccountName", serviceAccountName).
Directive("spec.template.spec.containers", "patchKey=name").
Set("spec.template.spec.containers[0].name", vela.Name()).
Set("spec.template.spec.containers[0].image", image).
SetIf(env.IsSet(), "spec.template.spec.containers[0].env", containerEnv).
SetIf(env.IsSet(), "spec.template.spec.containers[0].envFrom", envFromList).
SetIf(ports.IsSet(), "spec.template.spec.containers[0].ports", containerPorts).
Set("spec.template.spec.containers[0].volumeMounts", allMounts).
Set("spec.template.spec.volumes", podVolumes).
SetIf(tolerations.IsSet(), "spec.template.spec.tolerations", tolerationsArr).
If(cpu.IsSet()).
Set("spec.template.spec.containers[0].resources.requests.cpu", cpu).
Set("spec.template.spec.containers[0].resources.limits.cpu", cpu).
EndIf().
If(memory.IsSet()).
Set("spec.template.spec.containers[0].resources.requests.memory", memory).
Set("spec.template.spec.containers[0].resources.limits.memory", memory).
EndIf().
ConditionalStruct(requireAffinity.IsTrue(), "spec.template.spec.affinity.nodeAffinity", func(b *defkit.OutputStructBuilder) {
matchExpr := defkit.NewArrayElement().
Set("key", defkit.Lit("kubernetes.io/os")).
Set("operator", defkit.Lit("In")).
Set("values", defkit.Reference(`["linux"]`))
nodeTerm := defkit.NewArrayElement().
Set("matchExpressions", defkit.NewArray().Item(matchExpr))
b.Set("requiredDuringSchedulingIgnoredDuringExecution", defkit.NewArrayElement().
Set("nodeSelectorTerms", defkit.NewArray().Item(nodeTerm)))
})

tpl.Output(dep)

// HPA metrics — mix Item, ItemIf, and ForEachGuarded in one array.
cpuMetric := defkit.NewArrayElement().
Set("type", defkit.Lit("Resource")).
Set("resource", defkit.NewArrayElement().
Set("name", defkit.Lit("cpu")).
Set("target", defkit.NewArrayElement().
Set("type", defkit.Lit("Utilization")).
Set("averageUtilization", defkit.Lit(80))))

memMetric := defkit.NewArrayElement().
Set("type", defkit.Lit("Resource")).
Set("resource", defkit.NewArrayElement().
Set("name", defkit.Lit("memory")).
Set("target", defkit.NewArrayElement().
Set("type", defkit.Lit("Utilization")).
Set("averageUtilization", defkit.Lit(70))))

customElem := defkit.NewArrayElement().
Set("type", defkit.Lit("Pods")).
Set("pods", defkit.NewArrayElement().
Set("metric", defkit.NewArrayElement().
Set("name", defkit.Reference("m.name"))).
Set("target", defkit.NewArrayElement().
Set("type", defkit.Lit("AverageValue")).
Set("averageValue", defkit.Reference("m.value"))))

metrics := defkit.NewArray().
Item(cpuMetric).
ItemIf(memory.IsSet(), memMetric).
ForEachGuarded(customMetrics.IsSet(), customMetrics, customElem)

// HPA — apiVersion picked at runtime via VersionIf.
hpa := defkit.NewResourceWithConditionalVersion("HorizontalPodAutoscaler").
VersionIf(defkit.Lt(vela.ClusterVersion().Minor(), defkit.Lit(23)),
"autoscaling/v2beta2").
VersionIf(defkit.Ge(vela.ClusterVersion().Minor(), defkit.Lit(23)),
"autoscaling/v2").
Set("metadata.name", vela.Name()).
Set("spec.scaleTargetRef.apiVersion", defkit.Lit("apps/v1")).
Set("spec.scaleTargetRef.kind", defkit.Lit("Deployment")).
Set("spec.scaleTargetRef.name", vela.Name()).
Set("spec.minReplicas", minReplicas).
Set("spec.maxReplicas", maxReplicas).
Set("spec.metrics", metrics)

tpl.Outputs("hpa", hpa)

// Service — emitted only when ports is set, using the filtered list above.
svc := defkit.NewResource("v1", "Service").
Set("metadata.name", vela.Name()).
Set("spec.selector[app.oam.dev/component]", vela.Name()).
Set("spec.ports", servicePorts)
tpl.OutputsIf(ports.IsSet(), "service", svc)
}

func init() { defkit.Register(ScalableService()) }

Reproduce the CUE on the right with:

vela def validate-module ./my-platform
vela def gen-module ./my-platform -o ./generated-cue