导入配置
为了节省时间精力和减少犯错,CUE 有些命令可以导入和最小化已存在的配置生成 CUE 代码。
导入配置
cue import
将会导入 Yaml 和 JSON 的文件和目录生成 CUE 代码,
有一些参数可以控制 CUE 如何执行和组织输出。
让我们继续使用 Cuetorials 的 Kubernetes 例子,在 cuetorials.yaml
所在的目录执行下面的命令:
cue import -f -o cuetorials.cue -l 'strings.ToLower(kind)' -l 'metadata.name' -p cuetorials cuetorials.yaml
-f
覆盖输出文件
-o
设置输出的文件名
-l
告诉 CUE 如何命名值以避免冲突,这里我们将会用 <kind>: <name>: {...}
-p
指定 CUE 的 package: package cuetorials
最后一个参数是我们的原始文件,但是也可以是通配符或者目录(./...
)
cuetorials.cue
package cuetorials
deployment: cuetorials: {
apiVersion: "apps/v1"
kind: "Deployment"
metadata: {
name: "cuetorials"
namespace: "websites"
labels: app: "cuetorials"
}
spec: {
selector: matchLabels: app: "cuetorials"
strategy: {
type: "RollingUpdate"
rollingUpdate: {
maxSurge: 1
maxUnavailable: 0
}
}
minReadySeconds: 5
template: {
metadata: labels: app: "cuetorials"
spec: containers: [{
name: "website"
image: "us.gcr.io/hof-io--develop/cuetorials.com:manual"
imagePullPolicy: "Always"
ports: [{
containerPort: 80
protocol: "TCP"
}]
readinessProbe: {
httpGet: port: 80
initialDelaySeconds: 6
failureThreshold: 3
periodSeconds: 10
}
livenessProbe: {
httpGet: port: 80
initialDelaySeconds: 6
failureThreshold: 3
periodSeconds: 10
}
}]
}
}
}
service: cuetorials: {
apiVersion: "v1"
kind: "Service"
metadata: {
name: "cuetorials"
namespace: "websites"
labels: app: "cuetorials"
}
spec: {
selector: app: "cuetorials"
type: "ClusterIP"
ports: [{
port: 80
targetPort: 80
}]
}
}
ingress: cuetorials: {
apiVersion: "extensions/v1beta1"
kind: "Ingress"
metadata: {
name: "cuetorials"
namespace: "websites"
labels: app: "cuetorials"
annotations: {
"kubernetes.io/tls-acme" : "true"
"kubernetes.io/ingress.class" : "nginx"
"nginx.ingress.kubernetes.io/force-ssl-redirect" : "true"
"cert-manager.io/cluster-issuer" : "letsencrypt-prod"
"kubernetes.io/configuration-snippet" : """
location ~* \\ .(?:css|js|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1h;
access_log off;
add_header Cache-Control \" public \" ;
}
location / {
expires -1;
}
"""
}
}
spec: {
tls: [{
hosts: [
"cuetorials.com" ,
]
secretName: "cuetorials-tls"
}]
rules: [{
host: "cuetorials.com"
http: paths: [{
backend: {
serviceName: "cuetorials"
servicePort: 80
}
}]
}]
}
}
Go 生成 CUE
CUE 可以将 Go 的类型转换为 CUE 的定义,当你写好了代码并想验证 Yaml 或 JSON 能不能解析成对应的类型时非常有用。
j
你也可以通过 CUE 的结构定义生成 Go 的辅助函数来验证这些类型(详情:Cuelang docs , 需要写 Go 代码)
在我们使用 Go 生成 CUE 之前,我们需要新建一个文件,用于 CUE modules。
我们将在 module & package 这一节讨论 module 和 package,
目前我们先创建这个文件 cue.mod/module.cue
module : "github.com/hofstadter-io/cuetorials"
然后用下面的命令初始化 go.mod
文件
go mod init "github.com/hofstadter-io/cuetorials.com"
有了这些之后, 接下来我们要做:
把 Kubernetes API 包的代码下载下来(需要在本地)
从 Go 导入生成 CUE 定义
将 Kubernetes CUE 代码应用到我们的 Cuetorials 的 CUE 代码
验证我们的配置是否符合 Kubernetes 的规范
# "go get" 获取 Kubernetes API 相关的代码
go get k8s.io/api/...
# "cue go get" 导入到 CUE 的定义
cue get go k8s.io/api/...
# 检查 "cue get go" 在 cue.mod/gen 文件夹生成了什么
tree cue.mod/gen/
现在将以下几行加入到你的 cuetorials.cue
文件中:
package cuetorials // alread there
import (
apps "k8s.io/api/apps/v1"
core "k8s.io/api/core/v1"
extn "k8s.io/api/extensions/v1beta1"
)
deployment: [ string ]: apps.#Deployment
service: [ string ]: core.#Service
ingress: [ string ]: extn.#Ingress
// ...
现在试着运行 cue eval cuetorials.cue
,很可能会看到很多错误。
这是因为 Kubernetes 从几个地方接受 string 和 integer,为了修复这些问题,在 integer 前添加 IntVal: <#>
(或 StrVal:
,如果你使用的是包含 string 的其他 yaml)
deployment >>> rollingUpdate
deployment >>> httpGet
service >>> targetPort
ingress >>> servicePort
当 cue eval
没问题之后(打印出 CUE 的值)你可以运行 cue trim
来删除冗余的代码(基于最小化和默认值)。
在 CUE 的学习路径上有关于 高级修剪,基于 anti-unification
package cuetorials
import (
apps "k8s.io/api/apps/v1"
core "k8s.io/api/core/v1"
extn "k8s.io/api/extensions/v1beta1"
)
deployment: [ string ]: apps.#Deployment
service: [ string ]: core.#Service
ingress: [ string ]: extn.#Ingress
deployment: cuetorials: {
apiVersion: "apps/v1"
kind: "Deployment"
metadata: {
name: "cuetorials"
namespace: "websites"
labels: app: "cuetorials"
}
spec: {
selector: matchLabels: app: "cuetorials"
strategy: {
type: "RollingUpdate"
rollingUpdate: {
maxSurge: IntVal: 1
maxUnavailable: IntVal: 0
}
}
minReadySeconds: 5
template: {
metadata: labels: app: "cuetorials"
spec: containers: [{
name: "website"
image: "us.gcr.io/hof-io--develop/cuetorials.com:manual"
imagePullPolicy: "Always"
ports: [{
containerPort: 80
protocol: "TCP"
}]
readinessProbe: {
httpGet: port: IntVal: 80
initialDelaySeconds: 6
failureThreshold: 3
periodSeconds: 10
}
livenessProbe: {
httpGet: port: IntVal: 80
initialDelaySeconds: 6
failureThreshold: 3
periodSeconds: 10
}
}]
}
}
}
service: cuetorials: {
apiVersion: "v1"
kind: "Service"
metadata: {
name: "cuetorials"
namespace: "websites"
labels: app: "cuetorials"
}
spec: {
selector: app: "cuetorials"
type: "ClusterIP"
ports: [{
port: 80
targetPort: IntVal: 80
}]
}
}
ingress: cuetorials: {
apiVersion: "extensions/v1beta1"
kind: "Ingress"
metadata: {
name: "cuetorials"
namespace: "websites"
labels: app: "cuetorials"
annotations: {
"kubernetes.io/tls-acme" : "true"
"kubernetes.io/ingress.class" : "nginx"
"nginx.ingress.kubernetes.io/force-ssl-redirect" : "true"
"cert-manager.io/cluster-issuer" : "letsencrypt-prod"
"kubernetes.io/configuration-snippet" : """
location ~* \\ .(?:css|js|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1h;
access_log off;
add_header Cache-Control \" public \" ;
}
location / {
expires -1;
}
"""
}
}
spec: {
tls: [{
hosts: [
"cuetorials.com" ,
]
secretName: "cuetorials-tls"
}]
rules: [{
host: "cuetorials.com"
http: paths: [{
backend: {
serviceName: "cuetorials"
servicePort: IntVal: 80
}
}]
}]
}
}
你可能会说,“这个奇怪的 int 或 string 类型的端口并不能匹配我的 Kubernetes 文件”,确实会这样。
我们会在"集成"这一节会介绍如何让 OpenAPI 的规范生效和更多相关的内容。