流水线规则
概述
流水线规则(ClusterRule)是平台工程 CI/CD 实践中最基础的组成单元,也是最小的约束单元。平台提供了开箱即用的规则,也支持通过自定义规则满足约束场景。规则的约束场景覆盖代码扫描质量、安全扫描漏洞结果、必须引用既定的模板等各个方面,确保从代码提交到软件部署的每个环节都受到适当的监控和控制。
规则定义
规则字段说明
apiVersion: policies.katanomi.dev/v1alpha1
kind: ClusterRule
metadata:
name: template-label-check
spec:
when: # when 代表执行之前的校验,只有 when 校验通过,才会执行 filter 中的校验,否则跳过校验
cel: has(ce.data.raw.status.unitTestsResults)
filter: # filter 代表对模板信息的过滤
sql: templatelabels LIKE '%#label:$(params.key)=$(params.value)#%' # 支持 cloudevent sql 语法,详见 https://github.com/cloudevents/spec/blob/main/cesql/spec.md
params:
- name: key # 定义的参数名称
type: string # 定义的参数类型,目前支持 string
description: 模板需要包含的 label key # 参数描述when 代表进行数据校验前的条件校验,只有 when 中的条件通过,才会进行 filter 中的数据校验。
filter 为必填字段,如果缺失,资源创建时会报错,代表了对事件信息的过滤规则。
filter 与 when 中支持的过滤规则语法相同,支持的语法配置详见 filter 与 when 过滤语法说明 内容。
在过滤语句中,支持使用参数值,在 params 中指定参数,在 filter 或者 when 中通过 $(params.PARAMNAME) 引用。
filter 与 when 过滤语法说明
ClusterRule 中 when 与 filter 支持的过滤语法配置相同,存在如下几种配置:
| 过滤配置 | 数据类型 | 描述 |
|---|---|---|
| exact | map | 配置固定的 key value |
| prefix | map | 配置 key 对应值中,以 value 字符开头的 |
| suffix | map | 配置 key 对应值中,以 value 字符结尾的 |
| sql | string | 通过 CloudEvents SQL 对数据进行过滤 |
| cel | string | 通过 Common Expression Language 对数据 进行过滤 |
注意:
-
exact、prefix 与 suffix 使用场景极少,拓展性较差,实现的功能通过 cel 和 sql 可以完全覆盖。
-
更推荐使用 CloudEvents SQL 和 Common Expression Language 进行事件过滤,这里也主要介绍这两种过滤方式的使用语法。
CloudEvents SQL 语法
CloudEvents SQL 的详细文档参见 cesql-spec 。
其中包含的常见操作如下:
| 操作符 | 描述 | 示例 |
|---|---|---|
| EXISTS | 事件中某个 key 必须存在 | EXISTS id |
| IN | NOT IN | attribute 的值是否在列表中 | specversion IN ('1.0', '1.2') |
| LIKE | NOT LIKE | attribute 是否符合 SQL % 代表 0、1 多个字符 _ 代表单个字符 |
templaterefs LIKE '%#resolver:katanomi.dev.clustertemplate |name:go-build#%' |
| NOT | 非操作 | NOT EXISTS other |
| AND | OR | XOR | 与 或 异或 关系 | EXISTS id AND EXISTS specversion |
Common Expression Language 语法
Common Expression Language 的拓展性比 CloudEvents SQL 的拓展性更强,可以对多层级嵌套的字段做判断。
Common Expression Language 语法可以参考该表格中提供的 过滤能力 。
常见操作如下:
| 过滤配置 | 数据类型 | 描述 | ||
|---|---|---|---|---|
| == 与 != | 判断是否相等 不等 | ce.data.raw.spec.templates[0].metadata.labels.testlabel == ’test' | ||
| size | 判断 map 与 list 数据个数 | size(ce.data.raw.spec.templates[0][‘metadata’][’labels’]) == 4 | ||
| contains | 判断字符串包含 | ce.data.raw.spec.templates[0].metadata.labels.testlabel.contains(’test’) | ||
| endsWith | 判断字符串是否以某个字符串开头 | ce.data.raw.spec.templates[0].metadata.labels.testlabel.endsWith('st') |
||
| in | 判断列表或者字典中是否包含某元素 | ce.data.raw.spec.templates[0]['metadata']['labels']['katanomi.dev/source'] in ['system','local'] |
||
| matches | 判断字符串是否正则匹配 | ce.data.raw.spec.templates[0].metadata.annotations['translation.katanomi.dev/description.en'].matches('.*erform Go unit testing') |
||
| && | 对两个判断条件取 && | ce.data.raw.spec.tasks[0].params[5].value[0] == 'passed-tests-rate=100' && ce.data.raw.spec.tasks[0].params[5].value[1] == 'lines-coverage=0' |
||
| || | 对两个判断条件取 || | `ce.data.raw.spec.tasks[0].params[5].value[0] == ‘passed-tests-rate=100’ | ce.data.raw.spec.tasks[0].params[5].value[1] == ’lines-coverage=0'` |
自定义函数
由于 CEL 对数组类型数据的过滤,支持不够,目前只能通过制定下标的方式来选中某个数组元素。
我们提供了自定义函数 listValidate 来处理数组类型的数据,可以对所有数组元素的字段进行处理。
比如通过 ce.data.raw.status.codeScanResults.listValidate('metrics.branch.coverage.total', '>' ,'50.00') 校验所有的代码扫描覆盖率必须大于 50%。
exact/prefix/suffix 语法
通过指定 map 来匹配事件中的信息,使用 exact 完成精准匹配,比如校验事件中包含 subject: defult/build/policy 的数据:
exact:
subject: defult/build/policy使用 prefix 完成数据前缀匹配,校验事件中包含 key 为 subject value 以 default 开头的信息:
prefix:
subject: defult使用 suffix 完成数据后缀匹配,校验事件中包含 key 为 subject value 以 policy 结尾的信息:
suffix:
subject: policy开箱即用的规则
平台提供了一些常用的内置规则,在策略中可以直接引用以下规则。
约束必须使用固定名称的模板
apiVersion: policies.katanomi.dev/v1alpha1
kind: ClusterRule
metadata:
name: template-name-check
spec:
params:
- name: templateName
type: string
description: filter with the template name
filter:
sql: templatenames LIKE '%#name:$(params.templateName)#%'约束必须使用包含固定 label 的模板
apiVersion: policies.katanomi.dev/v1alpha1
kind: ClusterRule
metadata:
name: template-label-check
spec:
params:
- name: key
type: string
description: 模板需要包含的 label key
- name: value
type: string
description: 模板需要包含的 label Value
filter:
sql: templatelabels LIKE '%#label:$(params.key)=$(params.value)#%'约束必须使用固定的 clustertemplate 模板(官方模板)
apiVersion: policies.katanomi.dev/v1alpha1
kind: ClusterRule
metadata:
name: template-clustertemplate-resolver-check
spec:
params:
- name: name
type: string
description: 模板名称
filter:
sql: templaterefs LIKE '%#resolver:katanomi.dev.clustertemplate|name:$(params.name)#%'目前官方模板中包含以下几种模板:
| Name | Description | Scope |
|---|---|---|
| go-build | 依次进行 Go 单元测试、Golangci-lint 代码扫描和构建,并构建镜像。扫描结果将上报至 SonarQube | 构建 |
| java-build | 使用 Maven 构建 Java 项目,并进行代码扫描和镜像构建 | 构建 |
| nodejs-build | 进行 Node.js 单元测试、SonarQube 代码扫描和构建,并构建镜像 | 构建 |
| update-deployment | 快速编排测试、预生产、发布上线流程。支持人工审批、漏洞扫描等配置 | 发布流水线 |
详细信息可以通过在集群中执行 kubectl get clustertemplate 查看。
约束必须使用固定的源中的模板文件
apiVersion: policies.katanomi.dev/v1alpha1
kind: ClusterRule
metadata:
name: template-gitsource-resolver-check
spec:
params:
- name: url
type: string
description: 模板所处的代码仓库
- name: revision
type: string
description: 模板代码中 revision
- name: pathInRepo
type: string
description: 模板所处代码仓库位置
filter:
sql: templaterefs LIKE '%#resolver:katanomi.dev.gitsource|pathInRepo:$(params.pathInRepo)|revision:$(params.revision)|url:$(params.url)#%'自定义规则
平台规则机制设计上的高度灵活性支持按需自定义。在自定义规则时,需要了解 policyRun 中 cloudEvent 的信息,基本原理是使用 ceSQL 和 CEL 语句对 cloudEvent 中的 data.raw 数据进行逻辑处理。
注意:
- celSQL 目前还无法处理 data.raw 中的数据,只能处理 cloudEvent 中其他的数据。
- data.raw 里可能是 build 或 buildRun 资源的定义,buildRun 中包含的 buildRun result 可实现基于任务的执行结果进行规则配置,从而把控业务侧的单元测试、代码扫描、镜像漏洞等交付质量。
- 目前自定义规则适用于构建,不适用于发布流水线。
PolicyRun
PolicyRun 是执行策略校验过滤的主体资源,该资源存储待校验数据与 phase 信息用来进行策略校验。
校验原理如下:
- 首先获取 PolicyRun 所在命名空间所有的 Policy。
- 通过
spec.phasespec.source中的信息过滤出对应的 Policy。 - 使用 Policy 中引用的 ClusterRule 的 when、filter 配置对 PolicyRun 中
spec.cloudEvent中的信息进行校验。 - 返回校验结果,如果校验成功,认为通过该策略,如果校验失败,认为策略校验失败。
字段说明
PolicyRun 中存在 spec.phase spec.cloudEvent spec.raw spec.source 四个重要配置字段。
其中 spec.phase 代表了 policyRun 对应的策略校验阶段,执行校验时,会使用该策略值去过滤包含该 phase 值的 policy。
spec.raw 存储了资源的 yaml 定义信息,这部分信息会同步到 spec.cloudEvent.data.raw 中来支持更细致的规则校验。
spec.source 存储了 policyRun 对应的资源信息,包括 kind apiVersion 以及 metadata 的信息,这部分信息与 policy 中 scope 的 kind apiVersion nameRegex 过滤条件对应。
spec.cloudEvent 存储了资源的 yaml 定义信息以及 CloudEvent 的元信息,另外在某些场景下还存储了一些内置的格式化的信息。
policyRun 资源示例如下:
apiVersion: policies.katanomi.dev/v1alpha1
kind: PolicyRun
metadata:
name: template-zg2xw-o1tnvs
namespace: devops
spec:
# policyRun 对应的阶段
phase: BuildRunResultChanged
# 事件信息
cloudEvent:
# 事件元信息
source: /apis/builds.katanomi.dev/v1alpha1/namespaces/devops/build/template
specversion: "1.0"
subject: devops/build/template
type: dev.katanomi.cloudevents.builds.katanomi.dev.build.policycheck.v1alpha1
# 内置格式化信息
templatenames: "#name:go-build#"
templatelabels: "#label:core.katanomi.dev/template.kind=build.builds.katanomi.dev#label:core.katanomi.dev/template.type=snippets#label:katanomi.dev/source=system#"
templaterefs: "#resolver:katanomi.dev.clustertemplate|name:go-build#"
# buildSpec buildRun 或者 delivery 资源定义信息
data:
raw:
apiVersion: builds.katanomi.dev/v1alpha1
kind: Build
metadata:
name: check
namespace: default
spec: ...
status: ...
# buildSpec buildRun 或者 delivery 资源定义信息
raw:
apiVersion: builds.katanomi.dev/v1alpha1
kind: Build
metadata:
name: check
namespace: default
spec: ...
status: ...
status:
checkStatus: # 包含所有对策略的检查结果
message: "Check Completed: 1 (Succeed: 0, Failed 1, Unknown 0)" # 检查汇总信息
policies: # 所有涉及到的 policy 的集合
- checks: # 某个 policy 检查详情
- params:
- name: templateName
value: java
ruleRef:
kind: ClusterRule
name: template-name-check
policyRef: # policy 相关信息
apiVersion: policies.katanomi.dev/v1alpha1
kind: Policy
name: template-name-check
namespace: devops
status: "False" # 当前 Policy 检查结果
status: "False" # 最终检查结果
type: Succeeded
conditions: # 检查 condition 信息
- lastTransitionTime: "2023-09-13T23:39:55Z"
status: "True"
type: CheckReady
- lastTransitionTime: "2023-09-13T23:39:55Z"
status: "True"
type: PolicyFilterReady
- lastTransitionTime: "2023-09-13T23:39:55Z"
status: "True"
type: SucceededcloudEvent 详细说明
cloudEvent 字段存储的信息是真正会被用来执行策略校验的信息,编写 clusterRule 校验规则时需要了解该部分内容,因此对该信息进行详细说明。
cloudEvent 主要包括三部分内容,事件元信息、资源定义信息以及内置格式化数据。
事件元信息
记录了事件的基本信息比如 id source speceversion 等,该数据一般不会用于过滤配置,目前包含的信息如下所示。
datacontenttype: application/json
id: 365829cb-ef85-4994-b917-6326ce84c188
source: /apis/builds.katanomi.dev/v1alpha1/namespaces/global/build/policy
specversion: "1.0"
subject: default/build/policy
type: dev.katanomi.cloudevents.builds.katanomi.dev.build.policycheck.v1alpha1更多事件相关信息可以参见 CloudEvent 相关 spec 文档
资源的定义信息
不同资源在不同阶段创建的 PolicyRun CloudEvent 包含的资源定义的信息不同,详细信息如下表所示。
| 资源 | 阶段 | 描述 | 其他 | 资源定义 |
|---|---|---|---|---|
| 构建 | BuildRunDefinitionReady | 构建定义准备完成时 | 默认策略 | Build + BuildSpec(Build 资源类型以及 spec 中的构建流程详细定义信息) |
| 构建 | BuildRunResultChanged | 构建产生结果时 | BuildRun | |
| 构建 | BuildRunCompleted | 构建结束时 | BuildRun | |
| 发布流水线 | DeliveryRunDefinitionReady | 发布定义准备完成时 | 默认策略 | Delivery + DeliverySpec(Delivery 资源类型以及 spec 中的发布流程详细定义信息) |
其中 Build + BuildSpec 包含 Build 的 TypeMeta ObjectMeta 以及详细的 BuildSpec 信息(如果是引用模板,这里会存储模板渲染后的信息)。
BuildSpec 主要包含了 tasks templates 的相关信息,示例如下:
apiVersion: builds.katanomi.dev/v1alpha1
kind: Build
metadata:
annotations:
builds.katanomi.dev/trigger-ignore-message-migrated: "true"
cpaas.io/displayName: ""
integrations.katanomi.dev/integration: gitlab-a5fw2h
name: template-check
namespace: default
spec:
# buildSpec 信息,主要包含 tasks 构建流程定义,如果引用模板则包含模板信息与渲染后的 tasks 流程定义信息
tasks:
- name: go-unit-test
params:
- name: command
value: |
go test -v -json -coverprofile=test-output/coverage-report > test-output/test-report.json
taskRef:
kind: ClusterTask
name: go-unit-test
timeout: 30m0s
workspaces:
- name: source
workspace: source
templates:
- metadata:
labels:
core.katanomi.dev/template.kind: build.builds.katanomi.dev
core.katanomi.dev/template.type: snippets
katanomi.dev/source: system
name: go-build
params:
- name: tool-image
value: build-harbor.alauda.cn/devops/builder-go:1.17-ubuntu-ece13f7d
templateRef:
params:
- name: name
value: go-build
resolver: katanomi.dev.clustertemplateDelivery + DeliverySpec 包含 Delivery 的 TypeMeta ObjectMeta 以及详细的 DeliverySpec 信息(如果是引用模板,这里会存储模板渲染后的信息)。
DeliverySpec 包含了 configs params templates stages 的相关信息,示例如下:
apiVersion: deliveries.katanomi.dev/v1alpha1
kind: Delivery
metadata:
name: delivery
namespace: default
spec:
configs:
stages:
- context:
environmentSpec:
clusterRef:
apiVersion: clusterregistry.k8s.io/v1alpha1
kind: Cluster
name: global
namespace: cpaas-system
namespaceRef:
name: global
name: sit
timeout: 0s
params:
- artifact:
annotations:
integrationClassName: harbor
secretRef:
name: harbor-build
namespace: cpaas-system-global-credentials
type: OCIContainerImage
uri: build-harbor.alauda.cn/test/minio/minio/minio
default: RELEASE.2023-12-13T23-28-55Z
name: teste
type: string
stages:
- context:
environmentSpec:
clusterRef:
apiVersion: clusterregistry.k8s.io/v1alpha1
kind: Cluster
name: global
namespace: cpaas-system
namespaceRef:
name: global
name: sit
params:
- name: artifact
value: []
stageRef:
kind: ClusterStage
name: deployment-update
timeout: 0s
templates:
- metadata:
labels:
core.katanomi.dev/template.kind: delivery.deliveries.katanomi.dev
core.katanomi.dev/template.type: snippets
katanomi.dev/source: system
name: update-deployment
params:
- name: deploy-name
value: test
templateRef:
params:
- name: name
value: update-deployment
resolver: katanomi.dev.clustertemplateBuildRun 与集群中的 BuildRun 资源一致,在 status 中包含了详细的结果信息,比如代码扫描覆盖率,镜像扫描结果,结果部分的信息在 BuildRun result 说明 进行详细说明。
资源的示例 yaml 如下所示:
apiVersion: builds.katanomi.dev/v1alpha1
kind: BuildRun
metadata:
name: katanomi
namespace: devops
spec:
buildRef:
name: katanomi
namespace: devops
git:
revision: refs/heads/release-3.15
serviceAccount:
name: ""
status: ""
status:
codeLintResults:
- issues:
count: 0
name: golangci-lint
result: Succeeded
codeScan:
reportWebURL: https://build.xxx/dashboard
result: Succeeded
taskID: AYxsGlHxxx
codeScanResults:
- metrics:
branch:
coverage:
new: "0.00"
total: "53.50"
duplications:
new: "0.00"
total: "0.00"
codeSize:
linesOfCode: 12624
languages:
- go
- xml
ratings:
maintainability:
issues: 13
rate: A
reliability:
issues: 0
rate: A
securityHotspots:
issues: 0
rate: A
vulnerability:
issues: 0
rate: A
target:
coverage:
new: ""
total: ""
duplications:
new: ""
total: ""
name: code-scan
projectID: gitlab.xxx.cn-devops-katanomi
reportURL: https://build.xxx/dashboard
result: Succeeded
taskID: AYxsGlHha7lGyuc9WiM-
unitTestsResults:
- coverage:
lines: "47.8"
name: golang-test
testResults:
failed: 0
passed: 267
passedTestsRate: "100.00"
skipped: 0
type: unitTest内置格式化数据
这部分数据包含了引用模板的标签,模板名称,引用的官方模板,引用源管理中模板的信息,主要存储了构建或者发布使用的模板的信息。
这部分内容目前只有在策略 phase 为 BuildRunDefinitionReady 与 DeliveryRunDefinitionReady 时在 cloudEvent 中产生,其他阶段目前不会产生该数据。
包含的详细信息如下所示:
templatelabels:'#label:core.katanomi.dev/template.kind=build.builds.katanomi.dev#label:katanomi.dev/source=system#'
templatenames: '#name:go-build#'
templaterefs: '#resolver:katanomi.dev.clustertemplate|name:go-build#'
templaterefs: "#resolver:katanomi.dev.gitsource|pathInRepo:x.yaml|revision:/master|url:https://gitlab.cn/devops/test#"不同字段对应的含义如下:
| Name | Description | Example |
|---|---|---|
| templatenames | 存放引用的模板名称 | #name:go-build# |
| templatelabels | 存放引用的模板的 labels | #label:core.katanomi.dev/template.kind=build.builds.katanomi.dev#label:core.katanomi.dev/template.type=snippets#label:katanomi.dev/source=system# |
| templaterefs | 存放引用模板的具体信息,包括使用源中的模板以及使用官方模板 | #resolver:katanomi.dev.gitsource|pathInRepo:java.yaml|revision:/refs/heads/master|url:https://gitlab.cn/devops/test# 或者 #resolver:katanomi.dev.clustertemplate|name:go-build# |
BuildRun result 说明
大多数场景下,我们需要对 BuildRun 执行过程中产生的结果进行校验,了解 BuildRun 中结果的结构,有助于我们编写正确的 ClusterRule。
目前构建存在的结果有如下几种:
镜像扫描结果
镜像扫描漏洞信息,存在的资源 yaml 示例如下:
status:
vulnScanResults: # 镜像漏洞信息
- name: image-vlun # task 名称
result: Successed # 扫描结果
targets:
- type: ContainerImage
uri: build.alauda.cn/nginx:harbor@sha256:fe98dxx # 镜像ID
highCount: 20 # high 级别漏洞数目
lowCount: 5 # low 级别漏洞数目
criticalCount: 3 # critical 级别漏洞数目
mediumCount: 31 # medium 级别漏洞数目
unknownCount: 13 # unknown 级别漏洞数目
cvss: # 漏洞评分系统
score: "9.8" # 最高漏洞的分数
severity: Critical # 最高漏洞严重性
source: nvd # 最高漏洞的 CVSS 分数来源code lint 结果
code lint 包含了代码 lint 扫描结果信息,存在的资源 yaml 示例如下:
status:
codeLintResults: # code lint 结果
- issues:
count: 0 # 问题数目
name: golangci-lint # task 名称
result: Succeeded # 结果状态,可选值为 Succeeded Failed Canceled代码扫描结果
SonarQube 扫描结果,存在的资源 yaml 示例如下:
status:
codeScanResults: # 代码扫描结果
- name: code-scan # task 名称
projectID: gitlab-ce.alauda.cn # 扫描项目 ID
reportURL: https://xxxx # 代码扫描地址
result: Succeeded # 结果状态,可选值为 Succeeded Failed Canceled
taskID: AYxr372ra7lGyuc9WiMQ # 扫描任务 ID
metrics: # 扫描结果
branch: # 当前分支扫描结果
coverage: # 覆盖率信息
new: "0.00"
total: "53.50"
duplications: # 重复率信息
new: "0.00"
total: "0.00"
target: # 目标分支扫描结果
coverage: # 覆盖率信息
new: ""
total: ""
duplications: # 重复率信息
new: ""
total: ""
codeSize: # 代码行数
linesOfCode: 12624
languages: # 包含语言
- go
- xml
ratings: # 代码评级
maintainability: # 可维护性
issues: 13 # 代码异味数目
rate: A # 评级
reliability: # 可靠性
issues: 0 # 缺陷数目
rate: A # 评级
securityHotspots: # 审查安全热点
issues: 0 # 问题数目
rate: A # 评级
vulnerability: # 漏洞
issues: 0 # 漏洞数目
rate: A # 评级单元测试结果
单元测试结果字段信息,存在的资源 yaml 示例如下:
status:
unitTestsResults: # 代码测试结果
- name: golang-test # 任务名称
type: unitTest # 测试类型
coverage: # 测试覆盖率信息
lines: "47.8" # 行覆盖率
branches: "44.3" # 分支覆盖率
testResults: # 测试结果
failed: 0 # 测试失败 case 数目
passed: 267 # 测试通过 case 数目
passedTestsRate: "100.00" # 测试覆盖率
skipped: 0 # 测试跳过 case 数目自定义规则示例:测试覆盖率必须大于一定阈值
基于前面的自定义规则机制创建一个自定义规则,该规则的使用场景:平台团队要求业务团队 SonarQube 扫描后的测试覆盖率必须大于一定阈值。
注意: 建议将定义好的规则文件保存到代码仓库中,便于管理。
apiVersion: policies.katanomi.dev/v1alpha1
kind: ClusterRule
metadata:
name: sonarqube-analysis-coverage
spec:
params:
- description:
name: coverage # 阈值变量名称
type: string
when:
cel: has(ce.data.raw.status.codeScanResults) # 当存在代码扫描结果时,其结果值需要满足 filter 的条件
filter:
# 测试覆盖率需要大于 params.coverage 变量值,本示例中将覆盖率值配置为变量形式,在策略中引用该规则时配置具体的阈值,也可以在规则中直接定义为具体的阈值,如 8.0
cel: ce.data.raw.status.codeScanResults.listValidate('metrics.branch.coverage.total', '>', $(params.coverage))