Cue Lang介紹

Cue Lang

Cue,是一種開源語言,用于定義轨功,生成和驗證各種數(shù)據(jù):配置榨乎,API,數(shù)據(jù)庫模式恨搓,代碼......。它能夠?qū)?shù)據(jù)的結(jié)構(gòu)、約束固棚、數(shù)值作為同一層級成員,從而簡化配置文件的生成仙蚜。
Cue教程

Cue格式說明

  1. 使用//進行單行注釋
  2. 對象被稱為結(jié)構(gòu)體
  3. 對象成員稱為結(jié)構(gòu)字段
  4. 對于沒有特殊字符的字段名此洲,可以省略引號
  5. 結(jié)構(gòu)字段后面無需,
  6. 在列表中的最后一個元素后放置,
  7. 最外層的{}可省略

例子:

str: "hello world"
num: 42
flt: 3.14

// Special field name (and a comment)
"k8s.io/annotation": "secure-me"

// lists can have different element types
list: [
    "a", "b", "c",
    1,
    2,
    3,
]

obj: {
    foo: "bar"
    // reuse another field?!
    L: list
}

Cue 結(jié)構(gòu)、約束委粉、數(shù)據(jù)

// 結(jié)構(gòu)
album: {
    title: string
    year: int
    live: bool
}
// 約束
album: {
    title: string
    year: >1950
    live: false
}
// 數(shù)據(jù)
album: {
    title: "Houses of the Holy"
    year: 1973
    live: false
}

Cue的最佳實踐:從開放的結(jié)構(gòu)模式開始呜师,限制上下文可能性,最終具體到數(shù)據(jù)實例贾节。
Cue哲學:為了保證唯一性汁汗,Cue的數(shù)據(jù)不會被覆蓋趟紊。

Cue核心規(guī)則

  1. 數(shù)據(jù)可被重復定義,但必須值保持一致
  2. 結(jié)構(gòu)字段可以被更強限制覆蓋
  3. 結(jié)構(gòu)的字段會被合并碰酝,如果是列表霎匈,必須嚴格匹配
  4. 規(guī)則可被遞規(guī)應用
hello: "world"
hello: "world"

// set a type
s: { a: int }

// set some data
s: { a: 1, b: 2 }

// set a nested field without curly braces
s: c: d: 3

// lists must have the same elements
// and cannot change length
l: ["abc", "123"]
l: [
    "abc",
    "123"
]

結(jié)構(gòu)

  1. 結(jié)構(gòu)并不會輸出
  2. 它的值可能是不確認、不完整的
  3. 字段必須完全

使用#mydef來定義結(jié)構(gòu)送爸,使用...來定義一個開放的結(jié)構(gòu)體

#Album: {
    artist: string
    title: string
    year: int

    // ...  uncomment to open, must be last
}

// This is a conjunction, it says "album" has to be "#Album"
album: #Album & {
    artist: "Led Zeppelin"
    title: "Led Zeppelin I"
    year: 1969

    // studio: true  (uncomment to trigger error)
}

#Person: {
 name: string
... // open struct
}

Jim: #Person & {
 name: "jim"
 age: 12
}

約束

約束與數(shù)值使用&字符進行連接時铛嘱,會將值進行校驗

// conjunctions on a field
n: int & >0 & <100
n: 23

// conjuction of schemas
val: #Def1 & #Def2
val: { foo: "bar", ans: 42 }

#Def1: {
    foo: string
    ans: int
}

#Def2: {
    foo: =~ "[a-z]+"
    ans: >0
}

替換

使用|可以實現(xiàn)支持多種結(jié)構(gòu)。同時它也可以為出錯值設置替換值

// disjunction of values (like an enum)
hello: "world" | "bob" | "mary"
hello: "world"

// disjunction of types
port: string | int
port: 5432

// disjunction of schemas
val: #Def1 | #Def2
val: { foo: "bar", ans: 42 }

#Def1: {
    foo: string
    ans: int
}

#Def2: {
    name: string
    port: int
}

默認值與可選

使用*來設置默認值袭厂, ?設置可選字段

s: {
    // field with a default
    hello: string | *"world" | "apple"
    // an optional integer
    count?: int
}

開放模式與封閉模式

開放模式意味著結(jié)構(gòu)可以擴展墨吓,關閉模式意味著不能擴展。 默認情況下纹磺,結(jié)構(gòu)是開放模式帖烘,定義是封閉模式。 可以通過定義的最后添加...來申明開放模式定義橄杨;另外通過過close強制為結(jié)構(gòu)體設置為關閉模式

// Open definition
#d: {
    foo: "bar"
    ... // must be last
}

// Closed struct
s: close({
    foo: "bar"
})

jim: {
  name: "Jim"
}
jim: {
  age: 12
}

推薦從基礎定義開始秘症,復用定義

在編寫Cue時,推薦從基礎定義開始式矫,這樣能夠有更好的復用能力乡摹。

#Base: {
    name: string
    kind: string
    ... // so it can be extended
}
#Meta: {
    // string and a semver regex
    version: string & =~"^v[0-9]+\\.[0-9]+\\.[0-9]+$"
    // list of strings
    labels: [...string]
}

#Permissions: {
    role: string
    public: bool | *false
}

// Building up a schema using a conjunction and embedding
#Schema: #Base & {
    // "embed" meta and permissions
    #Meta
    #Permissions
    // with no '...' this is final
}

value: #Schema & {
    name: "app"
    kind: "deploy"
    version: "v1.0.42"
    labels: ["server", "prod"]
    role: "backend"
    // public: false  (by default)
}

使用"""來定義多行字符串

str1: #"avoid using \ to "escape""#
str2: """
a nested multiline
string goes here
"""

List

List 可被定義為開放模式,這樣便可與其它數(shù)據(jù)進行合并采转,

empty: []
any: [...]
ints: [...int]
nested: [...[...string]]

opened: ints & [1,2,...]
closed: ints & [1,2,3]

// list of for constrained ints
ip: 4 * [uint8]
// sets the first element
tendot: ip & [10, ...uint8]
// uses constraint as second element
one72: ip & [172, >=16 & <=32, ...]

mixed: any & [...] & ["a",1, { foo: "bar" }]
join: [1,2] + [3,4]
Join: opened & join

Struct

結(jié)構(gòu)體是Cue的主要內(nèi)容聪廉,也是最終數(shù)據(jù)的輸出。如上介紹故慈,默認情況下它是開放模式板熊。除了使用Json類型形式進行設置值,還可通過級聯(lián):來設置察绷,如a: hello: "world"

// an open struct
a: {
    foo: "bar"
}

// shorthand nested field
a: hello: "world"

// a closed struct
b: close({
    left: "right"
})

模式匹配約束

模式匹配允許您為與模式匹配的標簽指定約束干签。可以將約束應用于字符串標簽克婶,并使用標識符來設置字段筒严。

#schema: {
    name: string
    ans: string
    num: int | *42
}

// match elem fields and alias labels to Name,
// unify with schema, set name to Name by label
elems: [Name=_]: #schema & { name: Name }

elems: {
    one: {
        ans: "solo"
        num: 1
    }
    two: {
        ans: "life"
    }
}

elems: other: { ans: "id", num: 23 }

表達式

  1. 引用字段丹泉,使用\(**)顯用其它字段
container: {
    repo: "docker.io/cuelang"
    image: "cue"
    version: "v0.3.0"
    full: "\(repo)/\(image):\(version)"
}

name: "Tony"
msg: "Hello \(name)"
// conver string to bytes
b: '\(msg)'
// convert bytes to string
s: "\(b)"
  1. Cue也能夠為通過\(**)來設置key
apps: ["nginx", "express", "postgres"]
#labels: [string]: string
stack: {
    for i, app in apps {
        "\(app)": {
            name: app
            labels: #labels & {
                app: "foo"
                tier: "\(i)"
            }
        }
    }
}
  1. List遍歷
    遍歷List數(shù)據(jù)格式如下:[ for key, val in <iterable> [condition] { production } ]
nums: [1,2,3,4,5,6]
sqrd: [ for _, n in nums { n*n } ]
even: [ for _, n in nums if mod(n,2) == 0 { n } ]

listOfStructs: [ for p, n in nums {
    pos: p
    val: n
}]

extractVals: [ for p, S in listOfStructs { S.val } ]
  1. 條件控制語句
    沒有else情萤,所有判斷都會被執(zhí)行
app: {
    name: string
    tech: string
    mem: int

    if tech == "react" {
        tier: "frontend"
    }
    if tech != "react" {
        tier: "backend"
    }

    if mem < 1Gi {
        footprint: "small"
    }
    if mem >= 1Gi && mem < 4Gi {
        footprint: "medium"
    }
    if mem  >= 4Gi {
        footprint: "large"
    }
}

標準庫

Cue的標準庫中包含了很多的幫助包(helper packages)。

  1. Encoding
package stdlib

import (
    "encoding/json"
)

data: """
{
    "hello": "world",
    "list": [ 1, 2 ],
    "nested": {
        "foo": "bar"
    }
}
"""

jval: json.Unmarshal(data)

val: {
    hello: "world"
    list: [1,2]
    nested: foo: "bar"
}

cjson: json.Marshal(val)
  1. Strings
package stdlib

import "strings"

s: "HelloWorld"

u: strings.ToUpper(s)
l: strings.ToLower(s)

line: "Cue stands for configure, unify, execute"
words: strings.Split(line, " ")
lined: strings.Join(words, " ")

haspre: strings.HasPrefix(line, "Cue")
index:  strings.Index(line, "unify")
  1. List
package stdlib

import "list"

l1: [1,2,3,4,5]
l2: ["c","b","a"]

// constrain length
l2: list.MinItems(1)
l2: list.MaxItems(3)

// slice a list
l3: list.Slice(l1, 2,4)

// get the sum and product
sum: list.Sum(l1)
prd: list.Product(l1)

// linear search for list (no binary)
lc: list.Contains(l1, 2)

// sort a list
ls: list.Sort(l2, list.Ascending)
l2s: list.IsSorted(l2, list.Ascending)
lss: list.IsSorted(ls, list.Ascending)

// Flatten a list
ll: [1,[2,3],[4,[5]]]
lf: list.FlattenN(ll, 1)
  1. Constrain
package stdlib

import (
    "net"
    "time"
)

// string with ip format
ip: net.IPv4
ip: "10.1.2.3"

// string with time format
ts: time.Format(time.ANSIC)
ts: "Mon Jan 2 15:04:05 2006"

使用Cue制作腳本命令工具

Cue 擁有制作腳本命令工具的功能摹恨,它有一個工具層筋岛,可用來執(zhí)行腳本、讀寫文件以及網(wǎng)絡訪問等晒哄。
規(guī)范:

  • 腳本文件以_tool.cue結(jié)尾
  • 執(zhí)行命令為cue cmd <name> or cue <name>
    例子:
  1. 腳本文件名為ex_tool.cue
package foo

import (
    "tool/cli"
    "tool/exec"
    "tool/file"
)

// moved to the data.cue file to show how we can reference "pure" Cue files
// city: "Amsterdam"

// A command named "prompter"
command: prompter: {

    // save transcript to this file
    var: file: *"out.txt" | string @tag(file) // you can use "-t flag=filename.txt" to change the output file, see "cue help injection" for more details

    // prompt the user for some input
    ask: cli.Ask & {
        prompt:   "What is your name?"
        response: string
    }

    // run an external command, starts after ask
    echo: exec.Run & {
        // note the reference to ask and city here
        cmd:    ["echo", "Hello", ask.response + "!", "Have you been to", city + "?"]
        stdout: string // capture stdout, don't print to the terminal
    }

    // append to a file, starts after echo
    append: file.Append & {
        filename: var.file
        contents: echo.stdout // becuase we reference the echo task
    }

    // also starts after echo, and concurrently with append
    print: cli.Print & {
        text: echo.stdout // write the output to the terminal since we captured it previously
    }
}
  • prompter為命令名
  • ask/echo/append/print為唯一標識
  • cli.Ask/exec.Run/file.Append為函數(shù),
  • &{...}為函數(shù)參數(shù)
  1. 創(chuàng)建data.cue
package foo

city: "Amsterdam"
  1. 運行:cue cmd prompter or cue prompter
$ cue cmd prompter
What is your name? he
Hello he! Have you been to Amsterdam?
$ cat out.txt
Hello he! Have you been to Amsterdam?

Tips

  • A & B === B & A
  • A === A
  • 路徑短寫:{a : {b: {c: 5}}} == a b c: 5
  • 多種類型:a | b | c
  • 默認值:number | *1
  • 算術(shù): 4 + 5
  • 變量引用:"Hello (person)"
  • 列表遍歷:[ x for x in y ]
  • cue 執(zhí)行 當前目錄下的cue文件及父目錄下同一個package的cue文件
  • cue ./... 以上目錄 + 遍歷當前目錄的子目錄下的cue文件
  • _開頭的變量不會在輸出結(jié)果中顯示睁宰,作為局部變量
  • [Name=_] 可用來定義一個模板肪获,其中Name匹配任意字段。例如:
    application: [Name=_]: {
      name: string | *Name
    }
    
  • | 可判斷是否存在柒傻。例如:if _variable != | { // ... }
  • 定義映射:map: [string]: string
  • 定義切片:slice: [...{name:string,value:string}]

實踐

Go To Cue
  1. 使用 cue import 將已有的yaml轉(zhuǎn)成Cue語言
$ cue import ./... -p kube -l '"\(strings.ToCamel(kind))" "\(metadata.name)"' -fR 
  1. 引入k8s資源的模塊
$ go mod init main
$ cue get go k8s.io/api/extensions/v1beta1 -v
  1. 導入k8s資源模塊孝赫,并創(chuàng)建資源
package kube
import (
  "k8s.io/api/core/v1"
  "k8s.io/api/extensions/v1beta1"
)
service <Name>: v1.Service
deployment <Name>: v1beta1.Deployment

參考文檔

cue torials
cue語法
cue語言入門

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市红符,隨后出現(xiàn)的幾起案子青柄,更是在濱河造成了極大的恐慌,老刑警劉巖预侯,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件致开,死亡現(xiàn)場離奇詭異,居然都是意外死亡萎馅,警方通過查閱死者的電腦和手機双戳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來糜芳,“玉大人飒货,你說我怎么就攤上這事∏涂ⅲ” “怎么了膏斤?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長邪驮。 經(jīng)常有香客問我莫辨,道長,這世上最難降的妖魔是什么毅访? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任沮榜,我火速辦了婚禮,結(jié)果婚禮上喻粹,老公的妹妹穿的比我還像新娘蟆融。我一直安慰自己,他們只是感情好守呜,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布型酥。 她就那樣靜靜地躺著,像睡著了一般查乒。 火紅的嫁衣襯著肌膚如雪弥喉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天玛迄,我揣著相機與錄音由境,去河邊找鬼。 笑死,一個胖子當著我的面吹牛虏杰,可吹牛的內(nèi)容都是我干的讥蟆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼纺阔,長吁一口氣:“原來是場噩夢啊……” “哼瘸彤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起笛钝,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤钧栖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后婆翔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拯杠,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年啃奴,在試婚紗的時候發(fā)現(xiàn)自己被綠了潭陪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡最蕾,死狀恐怖依溯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瘟则,我是刑警寧澤黎炉,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站醋拧,受9級特大地震影響慷嗜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丹壕,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一庆械、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧菌赖,春花似錦缭乘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邑时,卻和暖如春奴紧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刁愿。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工绰寞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留到逊,地道東北人铣口。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓滤钱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親脑题。 傳聞我的和親對象是個殘疾皇子件缸,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容