首页 / 平台管理 / 流水线管理 / 平台工程 CI/CD / 流水线规则

流水线规则

概述

流水线规则(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 对数据 进行过滤

注意:

  1. exact、prefix 与 suffix 使用场景极少,拓展性较差,实现的功能通过 cel 和 sql 可以完全覆盖。

  2. 更推荐使用 CloudEvents SQLCommon 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 数据进行逻辑处理。

注意:

  1. celSQL 目前还无法处理 data.raw 中的数据,只能处理 cloudEvent 中其他的数据。
  2. data.raw 里可能是 build 或 buildRun 资源的定义,buildRun 中包含的 buildRun result 可实现基于任务的执行结果进行规则配置,从而把控业务侧的单元测试、代码扫描、镜像漏洞等交付质量。
  3. 目前自定义规则适用于构建,不适用于发布流水线。

PolicyRun

PolicyRun 是执行策略校验过滤的主体资源,该资源存储待校验数据与 phase 信息用来进行策略校验。

校验原理如下:

  1. 首先获取 PolicyRun 所在命名空间所有的 Policy。
  2. 通过 spec.phase spec.source中的信息过滤出对应的 Policy。
  3. 使用 Policy 中引用的 ClusterRule 的 when、filter 配置对 PolicyRun 中 spec.cloudEvent 中的信息进行校验。
  4. 返回校验结果,如果校验成功,认为通过该策略,如果校验失败,认为策略校验失败。

字段说明

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: Succeeded

cloudEvent 详细说明

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.clustertemplate

Delivery + 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.clustertemplate

BuildRun 与集群中的 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))