cue-kind-definition
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCUE Kind Definition
CUE Kind定义
Kinds are the schema definitions that drive the entire grafana-app-sdk code generation pipeline. Each kind describes a Kubernetes-style resource type: its name, versions, and per-version schema. All Go types, TypeScript types, API clients, CRD manifests, and the AppManifest are generated from these CUE files.
Kinds是驱动整个grafana-app-sdk代码生成流程的模式定义。每个Kind描述一种Kubernetes风格的资源类型:其名称、版本以及每个版本的模式。所有Go类型、TypeScript类型、API客户端、CRD清单和AppManifest均由这些CUE文件生成。
Adding a Kind
添加Kind
Use the CLI to scaffold a kind before editing:
bash
grafana-app-sdk project kind add <KindName> --overwriteThis creates a file with scaffolding, field comments, and example values. Read the generated comments carefully — they explain every field's purpose.
.cueAlways usewhen re-running to regenerate scaffolding without losing manual additions.--overwrite
编辑前使用CLI搭建Kind的脚手架:
bash
grafana-app-sdk project kind add <KindName> --overwrite这会创建一个包含脚手架、字段注释和示例值的文件。请仔细阅读生成的注释——它们解释了每个字段的用途。
.cue重新运行时请始终使用参数,以在不丢失手动添加内容的情况下重新生成脚手架。--overwrite
Kind File Structure
Kind文件结构
grafana-app-sdk project kind addkinds/package kindskinds/
├── manifest.cue # App manifest + version list declarations
├── mykind.cue # Common (cross-version) kind metadata
└── mykind_v1alpha1.cue # v1alpha1 schema + codegen configFor multi-version kinds the additional version files sit alongside:
kinds/
├── manifest.cue
├── mykind.cue
├── mykind_v1alpha1.cue
└── mykind_v1.cueFor larger, more complex kind definitions users may choose to organise kinds into per-kind and per-version subdirectories, each with their own package. The default CLI output uses the flat layout above.
grafana-app-sdk project kind addkinds/package kindskinds/
├── manifest.cue # App清单 + 版本列表声明
├── mykind.cue # (跨版本)通用Kind元数据
└── mykind_v1alpha1.cue # v1alpha1模式 + 代码生成配置对于多版本Kind,额外的版本文件会放在同一目录下:
kinds/
├── manifest.cue
├── mykind.cue
├── mykind_v1alpha1.cue
└── mykind_v1.cue对于更大、更复杂的Kind定义,用户可以选择将Kind组织到按Kind和版本划分的子目录中,每个子目录有自己的包。默认CLI输出使用上述扁平布局。
CUE Kind Anatomy
CUE Kind结构解析
A complete kind definition has three layers:
一个完整的Kind定义包含三层:
1. Common kind metadata (shared across versions)
1. 通用Kind元数据(所有版本共享)
cue
// kinds/mykind.cue
package kinds
myKind: {
kind: "MyKind" // Required: the kind name (PascalCase)
// other cross-version fields (scope, pluralName, validation, mutation, conversion, etc.)
// See references/kind-layout.md for the full field reference
}cue
// kinds/mykind.cue
package kinds
myKind: {
kind: "MyKind" // 必填:Kind名称(大驼峰命名)
// 其他跨版本字段(作用域、复数名称、验证、变更、转换等)
// 完整字段参考请查看references/kind-layout.md
}2. Per-version schema (one file per version)
2. 每个版本的模式(每个版本对应一个文件)
Each version joins the common metadata with its own schema via CUE's operator:
&cue
// kinds/mykind_v1alpha1.cue
package kinds
myKindv1alpha1: myKind & {
// Version-specific schema
schema: {
// spec: desired state — set by users/clients, never by the operator
spec: {
title: string
description: string | *"" // optional with default
count: int & >=0
enabled: bool | *true
}
// status: observed state — written only by the operator/reconciler,
// never by users. Mirrors Kubernetes spec/status conventions.
status: {
lastObservedGeneration: int | *0
state: string | *""
message: string | *""
}
}
// Code generation config
codegen: {
ts: { enabled: true } // generate TypeScript types
go: { enabled: true } // generate Go types and client
}
}每个版本通过CUE的运算符将通用元数据与自身模式合并:
&cue
// kinds/mykind_v1alpha1.cue
package kinds
myKindv1alpha1: myKind & {
// 版本特定模式
schema: {
// spec:期望状态 — 由用户/客户端设置,绝不会由运算符设置
spec: {
title: string
description: string | *"" // 可选字段,默认值为空
count: int & >=0
enabled: bool | *true
}
// status:观测状态 — 仅由运算符/协调器写入,
// 用户不得写入。遵循Kubernetes的spec/status约定。
status: {
lastObservedGeneration: int | *0
state: string | *""
message: string | *""
}
}
// 代码生成配置
codegen: {
ts: { enabled: true } // 生成TypeScript类型
go: { enabled: true } // 生成Go类型和客户端
}
}3. App manifest (version registration)
3. App清单(版本注册)
Since all files share , version objects are referenced directly — no imports needed in the flat layout:
package kindscue
// kinds/manifest.cue
package kinds
App: {
appName: "my-app"
versions: {
"v1alpha1": {
schema: myKindv1alpha1
}
}
}由于所有文件共享包,版本对象可直接引用——扁平布局中无需导入:
package kindscue
// kinds/manifest.cue
package kinds
App: {
appName: "my-app"
versions: {
"v1alpha1": {
schema: myKindv1alpha1
}
}
}spec vs status
spec vs status
This distinction follows Kubernetes conventions exactly:
specspecspecspecstatusstatusstatusTypical fields:
statuscue
status: {
// Generation of the spec that was last successfully reconciled.
// Set to metadata.generation after a successful reconcile loop.
lastObservedGeneration: int | *0
// Human-readable summary of current state
state: string | *"" // e.g. "Ready", "Provisioning", "Error"
message: string | *"" // detail, especially on error
// References to objects created by the reconciler.
// e.g. the name of a ConfigMap or Deployment the reconciler provisioned.
provisionedConfigMap: string | *""
provisionedServiceAccount: string | *""
}Fields that belong in , not :
statusspec- Anything the operator computes or creates (IDs, names, URLs of provisioned resources)
- /
lastObservedGenerationobservedGeneration - (Kubernetes-style condition arrays)
conditions - Current health or lifecycle state (,
"Ready", etc.)"Degraded" - Timestamps of when the operator last acted
Fields that belong in , not :
specstatus- Everything the user configures as desired state
- References to existing resources the user wants the app to interact with (the operator looks these up, it doesn't create them)
这种区分完全遵循Kubernetes约定:
specspecspecspecstatusstatusstatus典型的字段:
statuscue
status: {
// 最后成功协调的spec版本。
// 成功完成协调循环后设置为metadata.generation。
lastObservedGeneration: int | *0
// 当前状态的可读摘要
state: string | *"" // 例如:"Ready"、"Provisioning"、"Error"
message: string | *"" // 详细信息,尤其是错误时
// 协调器创建的对象引用。
// 例如:协调器配置的ConfigMap或Deployment的名称。
provisionedConfigMap: string | *""
provisionedServiceAccount: string | *""
}属于而非的字段:
statusspec- 运算符计算或创建的任何内容(配置资源的ID、名称、URL)
- /
lastObservedGenerationobservedGeneration - (Kubernetes风格的条件数组)
conditions - 当前健康或生命周期状态(、
"Ready"等)"Degraded" - 运算符上次操作的时间戳
属于而非的字段:
specstatus- 用户配置的所有期望状态内容
- 用户希望应用与之交互的现有资源引用(运算符会查找这些资源,而非创建它们)
Type Definitions with #
#使用#
进行类型定义
#CUE supports named type definitions using the prefix inside a block. Each generates a named Go struct and TypeScript interface alongside the kind's type.
#schema#DefinitionSpeccue
schema: {
#Threshold: {
value: float & >=0
severity: "info" | "warning" | "critical"
message: string | *""
}
#ResourceRef: {
name: string & != ""
namespace: string | *"default"
}
spec: {
title: string & != ""
alertThreshold: #Threshold
thresholds: [...#Threshold] // list of a defined type
targetRef?: #ResourceRef // optional
}
}#schemaPrefer definitions when:
#- A struct is used in more than one field
- A struct is large or complex enough that inlining hurts readability
- A struct appears in a list ()
[...#MyType]
Inline structs are fine when:
- The struct is small and simple (2-3 fields) or shallow
- It is used in only one place and unlikely to be reused
Maps () and lists of scalars () are always fine inline.
{[string]: string}[...string]CUE支持在块内使用前缀进行命名类型定义。每个会与Kind的类型一起生成命名Go结构体和TypeScript接口。
schema##DefinitionSpeccue
schema: {
#Threshold: {
value: float & >=0
severity: "info" | "warning" | "critical"
message: string | *""
}
#ResourceRef: {
name: string & != ""
namespace: string | *"default"
}
spec: {
title: string & != ""
alertThreshold: #Threshold
thresholds: [...#Threshold] // 定义类型的列表
targetRef?: #ResourceRef // 可选字段
}
}#schema建议在以下场景使用定义:
#- 结构体在多个字段中使用
- 结构体较大或较复杂,内联会降低可读性
- 结构体出现在列表中()
[...#MyType]
以下场景适合内联结构体:
- 结构体小而简单(2-3个字段)或层级浅
- 仅在一个地方使用且不太可能复用
映射()和标量列表()始终适合内联。
{[string]: string}[...string]Schema Field Types
模式字段类型
CUE is a superset of JSON. Commonly used types and constraints:
cue
// Basic types
myString: string
myInt: int
myFloat: float
myBool: bool
myBytes: bytes
// Optional with default
name: string | *"default-value"
// Constraints (using & to intersect)
port: int & >=1 & <=65535
label: string & =~"^[a-z][a-z0-9-]*$" // regex constraint
// Enums (disjunctions)
status: "pending" | "active" | "archived"
// Maps (always fine inline)
labels: {[string]: string}
attrs: {[string]: _}
// Lists of scalars (fine inline)
tags: [...string]
// Optional field
description?: stringCUE是JSON的超集。常用类型和约束如下:
cue
// 基础类型
myString: string
myInt: int
myFloat: float
myBool: bool
myBytes: bytes
// 可选字段带默认值
name: string | *"default-value"
// 约束(使用&进行交集)
port: int & >=1 & <=65535
label: string & =~"^[a-z][a-z0-9-]*$" // 正则约束
// 枚举(析取)
status: "pending" | "active" | "archived"
// 映射(始终适合内联)
labels: {[string]: string}
attrs: {[string]: _}
// 标量列表(适合内联)
tags: [...string]
// 可选字段
description?: stringCustom Routes in CUE
CUE中的自定义路由
Routes can be defined at two levels. Both require corresponding Go handlers registered in .
app.go路由可在两个层级定义。两者都需要在中注册对应的Go处理器。
app.goKind-level routes
Kind级路由
cue
MyKind: {
kind: "MyKind"
schema: { ... }
routes: {
"/actions/process": {
"POST": {
name: "processMyKind" // unique within version; must start with a k8s verb
request: {
body: {
reason: string
}
}
response: {
jobId: string
status: string
}
}
}
}
}cue
MyKind: {
kind: "MyKind"
schema: { ... }
routes: {
"/actions/process": {
"POST": {
name: "processMyKind" // 版本内唯一;必须以k8s动词开头
request: {
body: {
reason: string
}
}
response: {
jobId: string
status: string
}
}
}
}
}Version-level routes
版本级路由
cue
versions: {
"v1alpha1": {
routes: {
namespaced: {
"/summary": {
"GET": {
name: "getNamespacedSummary"
response: { count: int }
}
}
}
cluster: {
"/health": {
"GET": {
name: "getHealth"
response: { status: string }
}
}
}
}
}
}After adding routes, run — routes are included in the AppManifest and will fail if a handler is missing.
grafana-app-sdk generateValidateManifestcue
versions: {
"v1alpha1": {
routes: {
namespaced: {
"/summary": {
"GET": {
name: "getNamespacedSummary"
response: { count: int }
}
}
}
cluster: {
"/health": {
"GET": {
name: "getHealth"
response: { status: string }
}
}
}
}
}
}添加路由后,运行——路由会被包含在AppManifest中,如果缺少处理器,会失败。
grafana-app-sdk generateValidateManifestVersion Compatibility Rules
版本兼容性规则
When a kind has multiple versions, fields declared in the common metadata object must match across all versions. Schema fields (inside ) can differ per version, but:
schema.spec- The field must be identical in every version
kind - Breaking changes (removing fields, changing types, adding required fields) must be introduced via a new version — never by modifying a stable version (,
v1)v2 - Use for server-managed fields; never put mutable server state in
statusspec
当一个Kind有多个版本时,通用元数据对象中声明的字段必须在所有版本中保持一致。模式字段(内的字段)可以因版本而异,但需遵循:
schema.spec- 字段在每个版本中必须完全相同
kind - 破坏性变更(删除字段、更改类型、添加必填字段)必须通过新版本引入——绝不要修改稳定版本(、
v1)v2 - 使用存储服务器管理的字段;绝不要将可变服务器状态放在
status中spec
Codegen Configuration
代码生成配置
Control what gets generated per kind per version:
cue
codegen: {
ts: { enabled: true | false } // TypeScript types
go: { enabled: true | false } // Go types + client
}Disabling for frontend-only apps avoids generating unused Go code. Disabling for backend-only resources reduces TypeScript bundle size. Both default to when omitted.
gotstrue控制每个Kind每个版本生成的内容:
cue
codegen: {
ts: { enabled: true | false } // TypeScript类型
go: { enabled: true | false } // Go类型 + 客户端
}对于仅前端应用,禁用可避免生成未使用的Go代码。对于仅后端资源,禁用可减小TypeScript包体积。省略时两者默认均为。
gotstrueAfter Editing Kinds
编辑Kind之后
Always run generate after any change to files:
.cuebash
grafana-app-sdk generateThe generated files in must never be edited manually — they are overwritten on every generate run.
pkg/generated/修改文件后请始终运行生成命令:
.cuebash
grafana-app-sdk generatepkg/generated/