API使用介绍

认证/授权

Results API 的参考实现期望从其运行的集群中获取集群生成的认证令牌。在大多数情况下,使用服务账户将是与 API 交互的最简单方法。

RBAC 授权用于控制对 API 资源的访问。

以下属性被识别:

属性
apiGroupsresults.tekton.dev
resourcesresults, records
verbscreate, get, list, update, delete

例如,一个只读角色可能如下所示:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: tekton-results-readonly
rules:
  - apiGroups: ["results.tekton.dev"]
    resources: ["results", "records"]
    verbs: ["get", "list"]

在参考实现中,所有权限都在命名空间范围内(这就是用作 API 父资源的内容)。

为了方便起见,以下[ClusterRoles]为常见的访问模式定义:

ClusterRole描述
tekton-results-readonly对所有Results API 资源的只读访问
tekton-results-readwrite包含 tekton-results-readonly + 创建或更新所有Results API 资源
tekton-results-admin包含 tekton-results-readwrite + 允许删除Results API 资源

模拟

Kubernetes 的模拟用于细化对 API 的访问。

如何使用模拟?

  • 在配置中将功能标志 AUTH_IMPERSONATE 设置为 true 运行 API 服务器。

  • 创建两个 ServiceAccount,一个用于管理模拟权限,另一个用于模拟用户的权限。

    kubectl create serviceaccount impersonate-admin -n tekton-pipelines
    
    kubectl create serviceaccount impersonate-user -n user-namespace
    
  • impersonate-admin 服务账户创建以下 ClusterRoleClusterRoleBinding

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: tekton-results-impersonate
    rules:
      - apiGroups: [""]
        resources: ["users", "groups", "serviceaccounts"]
        verbs: ["impersonate"]
      - apiGroups: ["authentication.k8s.io"]
        resources: ["uids"]
        verbs: ["impersonate"]
    
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: tekton-results-impersonate
    subjects:
      - kind: ServiceAccount
        name: impersonate-admin
        namespace: tekton-pipelines
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: tekton-results-impersonate
    
  • 最后,为 impersonate-user 服务账户创建 RoleBinding

    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: tekton-results-user
      namespace: user-namespace
    subjects:
      - kind: ServiceAccount
        name: impersonate-user
        namespace: user-namespace
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: tekton-results-readonly
    
  • 现在获取服务账户 impersonate-admin 的令牌并存储在变量中。

    token=$(kubectl create token impersonate-admin -n tekton-pipelines)
    
  • 然后可以按以下格式调用 API

    curl -s --cacert /var/tmp/tekton/ssl/tls.crt  \
      -H 'authorization: Bearer '${token} \
      -H 'Impersonate-User: system:serviceaccount:user-namespace:impersonate-user' \
      https://localhost:8080/apis/results.tekton.dev/v1alpha2/parents/user-namespace/results
    

如果 API 服务器使用 TLS,则需要提供 TLS 证书。

故障排除

可以运行以下命令来查询集群的权限。这对于调试权限被拒绝错误非常有用:

kubectl create --as=system:serviceaccount:tekton-pipelines:tekton-results-watcher -n tekton-pipelines -f - -o yaml << EOF
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
spec:
  resourceAttributes:
    group: results.tekton.dev
    resource: results
    verb: get
EOF
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
metadata:
  creationTimestamp: null
  managedFields:
  - apiVersion: authorization.k8s.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:spec:
        f:resourceAttributes:
          .: {}
          f:group: {}
          f:resource: {}
          f:verb: {}
    manager: kubectl
    operation: Update
    time: "2021-02-02T22:37:32Z"
spec:
  resourceAttributes:
    group: results.tekton.dev
    resource: results
    verb: get
status:
  allowed: true
  reason: 'RBAC: allowed by ClusterRoleBinding "tekton-results-watcher" of ClusterRole
    "tekton-results-watcher" to ServiceAccount "tekton-results-watcher/tekton-pipelines"'

过滤

Results API 的参考实现使用 CEL 作为过滤规范。过滤规范期望一个布尔Results值。本文档涵盖了一小部分对过滤Results和记录有用的 CEL。

Results

以下是Results JSON/protobuf 字段与 CEL 引用之间的映射:

字段CEL 引用字段描述
-parentResults的父(工作区/命名空间)名称。
uiduidResults的唯一标识符。
annotationsannotations添加到Results的注释。
summarysummaryResults的摘要。
createTimecreate_timeResults的创建时间。
updateTimeupdate_timeResults的最后更新时间。

summary.status 字段是一个枚举,必须在过滤表达式中不带引号('")使用。可能的值是:

  • UNKNOWN
  • SUCCESS
  • FAILURE
  • TIMEOUT
  • CANCELLED

记录和日志

以下是记录 JSON/protobuf 字段与 CEL 引用之间的映射:

字段CEL 引用字段描述
namename记录名称
data.typedata_type记录数据的类型标识符。请参见下面的值。
data.valuedata记录中包含的数据。在 JSON 和 protobuf 响应中,它表示为 base 64 编码的字符串。

data_typesummary.type(对于Results)的可能值是:

  • tekton.dev/v1beta1.TaskRunTASK_RUN
  • tekton.dev/v1beta1.PipelineRunPIPELINE_RUN
  • results.tekton.dev/v1alpha2.Log

记录中的data 字段

data 字段是对象清单的 base64 编码字符串。如果您直接使用 CLI、REST 或 gRPC 请求此数据,您将获得 base64 编码字符串。您可以使用 base64 -d 命令对其进行解码。这不是人类可读的,但您可以直接使用过滤器来过滤响应而无需解码。

以下是记录的 data 字段中包含的 JSON 对象的示例。这可以直接映射到我们通常使用的 YAML 表示法。

{
  "kind": "PipelineRun",
  "spec": {
    "timeout": "1h0m0s",
    "pipelineSpec": {
      "tasks": [
        {
          "name": "hello",
          "taskSpec": {
            "spec": null,
            "steps": [
              {
                "name": "hello",
                "image": "ubuntu",
                "script": "echo hello world!",
                "resources": {}
              }
            ],
            "metadata": {}
          }
        }
      ]
    },
    "serviceAccountName": "default"
  },
  "status": {
    "startTime": "2023-08-22T09:08:59Z",
    "conditions": [
      {
        "type": "Succeeded",
        "reason": "Succeeded",
        "status": "True",
        "message": "Tasks Completed: 1 (Failed: 0, Cancelled 0), Skipped: 0",
        "lastTransitionTime": "2023-08-22T09:09:31Z"
      }
    ],
    "pipelineSpec": {
      "tasks": [
        {
          "name": "hello",
          "taskSpec": {
            "spec": null,
            "steps": [
              {
                "name": "hello",
                "image": "ubuntu",
                "script": "echo hello world!",
                "resources": {}
              }
            ],
            "metadata": {}
          }
        }
      ]
    },
    "completionTime": "2023-08-22T09:09:31Z",
    "childReferences": [
      {
        "kind": "TaskRun",
        "name": "hello-hello",
        "apiVersion": "tekton.dev/v1beta1",
        "pipelineTaskName": "hello"
      }
    ]
  },
  "metadata": {
    "uid": "1638b693-844d-4f13-b767-d7d84ac4ab3d",
    "name": "hello",
    "labels": {
      "tekton.dev/pipeline": "hello"
    },
    "namespace": "default",
    "generation": 1,
    "annotations": {
      "results.tekton.dev/record": "default/results/1638b693-844d-4f13-b767-d7d84ac4ab3d/records/1638b693-844d-4f13-b767-d7d84ac4ab3d",
      "results.tekton.dev/result": "default/results/1638b693-844d-4f13-b767-d7d84ac4ab3d",
      "results.tekton.dev/resultAnnotations": "{\"repo\": \"tektoncd/results\", \"commit\": \"1a6b908\"}",
      "results.tekton.dev/recordSummaryAnnotations": "{\"foo\": \"bar\"}",
      "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"tekton.dev/v1beta1\",\"kind\":\"PipelineRun\",\"metadata\":{\"annotations\":{\"results.tekton.dev/recordSummaryAnnotations\":\"{\\\"foo\\\": \\\"bar\\\"}\",\"results.tekton.dev/resultAnnotations\":\"{\\\"repo\\\": \\\"tektoncd/results\\\", \\\"commit\\\": \\\"1a6b908\\\"}\"},\"name\":\"hello\",\"namespace\":\"default\"},\"spec\":{\"pipelineSpec\":{\"tasks\":[{\"name\":\"hello\",\"taskSpec\":{\"steps\":[{\"image\":\"ubuntu\",\"name\":\"hello\",\"script\":\"echo hello world!\"}]}}]}}}\n"
    },
    "managedFields": [
      {
        "time": "2023-08-22T09:08:59Z",
        "manager": "controller",
        "fieldsV1": {
          "f:metadata": {
            "f:labels": {
              ".": {},
              "f:tekton.dev/pipeline": {}
            }
          }
        },
        "operation": "Update",
        "apiVersion": "tekton.dev/v1beta1",
        "fieldsType": "FieldsV1"
      },
      {
        "time": "2023-08-22T09:08:59Z",
        "manager": "kubectl-client-side-apply",
        "fieldsV1": {
          "f:spec": {
            ".": {},
            "f:pipelineSpec": {
              ".": {},
              "f:tasks": {}
            }
          },
          "f:metadata": {
            "f:annotations": {
              ".": {},
              "f:results.tekton.dev/resultAnnotations": {},
              "f:results.tekton.dev/recordSummaryAnnotations": {},
              "f:kubectl.kubernetes.io/last-applied-configuration": {}
            }
          }
        },
        "operation": "Update",
        "apiVersion": "tekton.dev/v1beta1",
        "fieldsType": "FieldsV1"
      },
      {
        "time": "2023-08-22T09:08:59Z",
        "manager": "watcher",
        "fieldsV1": {
          "f:metadata": {
            "f:annotations": {
              "f:results.tekton.dev/record": {},
              "f:results.tekton.dev/result": {}
            }
          }
        },
        "operation": "Update",
        "apiVersion": "tekton.dev/v1beta1",
        "fieldsType": "FieldsV1"
      },
      {
        "time": "2023-08-22T09:09:31Z",
        "manager": "controller",
        "fieldsV1": {
          "f:status": {
            ".": {},
            "f:startTime": {},
            "f:conditions": {},
            "f:pipelineSpec": {
              ".": {},
              "f:tasks": {}
            },
            "f:completionTime": {},
            "f:childReferences": {}
          }
        },
        "operation": "Update",
        "apiVersion": "tekton.dev/v1beta1",
        "fieldsType": "FieldsV1",
        "subresource": "status"
      }
    ],
    "resourceVersion": "1567",
    "creationTimestamp": "2023-08-22T09:08:59Z"
  },
  "apiVersion": "tekton.dev/v1beta1"
}

现在可以使用点表示法访问所需字段,并将 data 视为父对象。例如:

目的过滤表达式
PipelineRun 的名称data.metadata.name
PipelineRun 中使用的 ServiceAccount 的名称data.spec.serviceAccountName
PipelineRun 中第一个任务的名称data.spec.pipelineSpec.tasks[0].name
PipelineRun 的开始时间data.status.startTime
PipelineRun 的标签data.metadata.labels
PipelineRun 的注释data.metadata.annotations
PipelineRun 的特定注释data.metadata.annotations['foo']

日志中的data 字段

data 字段是类型为 Log 的自定义对象的 base64 编码。访问字段与记录类似。以下是日志的 data 字段中包含的 JSON 对象的示例。

{
  "kind": "Log",
  "spec": {
    "type": "File",
    "resource": {
      "uid": "dbe14a60-1fc8-458e-a49b-264771557c3e",
      "kind": "TaskRun",
      "name": "hello",
      "namespace": "default"
    }
  },
  "status": {
    "path": "default/2d27dde5-e201-35f9-b658-2456f2955903/hello-log",
    "size": 83
  },
  "metadata": {
    "uid": "2d27dde5-e201-35f9-b658-2456f2955903",
    "name": "hello-log",
    "namespace": "default",
    "creationTimestamp": null
  },
  "apiVersion": "results.tekton.dev/v1alpha2"
}

以下是使用点表示法访问日志字段的一些示例:

目的过滤表达式
创建此日志的运行的类型data.spec.resource.kind
日志的大小data.status.size
创建此日志的运行的名称data.spec.resource.name

如何创建 CEL 过滤表达式

CEL 表达式由标识符、文字、运算符和函数组成。在这里,我们将学习如何使用上述字段创建 CEL 过滤表达式。这不是 CEL 表达式的详尽列表。有关更多信息,请参阅 CEL 规范

访问字段

CEL 表达式通常与 JSON/protobuf 字段一一对应。在 Tekton Results中,我们创建了一些额外的别名以便于访问。您可以在上表中看到所有这些。除此之外,您可以使用点表示法访问 JSON/protobuf 对象的任何字段。请参阅下表中的示例。

目的过滤表达式描述
Results的状态summary.statusResults的 statussummary 对象的子对象。
记录的数据类型data_typedata_type 是记录的 data.type 的定义别名。
Results的数据类型summary.typeResults的 typesummary 的子对象。
从状态中获取运行的任务的第一个步骤的名称data.status.taskSpec.steps[0].nameJSON 路径为 data -> status -> taskSpec -> steps -> 0 -> name

使用运算符

现在我们可以访问字段了,您可以使用运算符创建过滤器。以下是可以在 CEL 表达式中使用的运算符列表:

运算符描述示例
==等于data_type == "tekton.dev/v1beta1.TaskRun"
!=不等于summary.status != SUCCESS
IN在列表中data.metadata.name in ['hello', 'foo', 'bar']
!否定!(data.status.name in ['hello', 'foo', 'bar'])
&&逻辑与data_type == "tekton.dev/v1beta1.TaskRun" && name.startsWith("foo/results/bar")
||逻辑或data_type == "tekton.dev/v1beta1.TaskRun" || data_type == "tekton.dev/v1beta1.PipelineRun"
+, -, *, /, %算术运算符data.status.completionTime - data.status.startTime > duration('5m')
>, >=, <, <=比较运算符data.status.completionTime > data.status.startTime

使用函数

在 CEL 表达式中可以使用许多函数。以下是可以在 CEL 表达式中使用的函数列表。函数参数中的字符串表示参数的预期类型:

函数描述示例
startsWith('string')检查字符串是否以某个前缀开头data.metadata.name.startsWith("foo")
endsWith('string')检查字符串是否以某个后缀结尾data.metadata.name.endsWith("bar")
contains('string')检查字段是否存在或对象是否包含某个键或值data.metadata.annotations.contains('bar')
timestamp('RFC3339-timestamp')用于比较时间戳data.status.startTime > timestamp("2021-02-02T22:37:32Z")
getDate()从时间戳中返回日期data.status.completionTime.getDate() == 7
getDayOfWeek(), getDayOfMonth(), getDayOfYear()从时间戳中返回星期几、月份中的某一天或年份中的某一天data.status.completionTime.getDayOfMonth() == 7
getFullYear()从时间戳中返回年份data.status.startTime.getFullYear() == 2023
getHours(), getMinutes(), getSeconds()从时间戳中返回小时、分钟或秒数data.status.completionTime.getHours() >= 9
string('input')将有效输入转换为字符串string(data.status.completionTime) == "2021-02-02T22:37:32Z"
matches('regex')检查字符串是否匹配正则表达式name.matches("^foo.*$")

您还可以嵌套函数调用并混合运算符以创建复杂的过滤表达式。请确保为函数使用正确类型的参数。您可以在 CEL 规范中看到更详尽的函数参考。上面提到的函数是最常用和有效的。

使用 gRPC 的 CEL 过滤表达式

您可以通过指定 filter=<cel-expression> 将过滤器传递给 gRPC 请求。请在查询中使用正确的引号或在需要时使用 \。请参阅以下示例:

grpc_cli call --channel_creds_type=ssl \
  --ssl_target=tekton-results-api-service.tekton-pipelines.svc.cluster.local \
  --call_creds=access_token=$ACCESS_TOKEN \
  localhost:8080 tekton.results.v1alpha2.Results.ListResults \
  'parent:"default",filter:"data_type==TASK_RUN"'

使用 REST 的 CEL 过滤表达式

您可以通过在查询中指定 filter=<cel-expression> 将过滤器传递给 REST 请求。请参阅以下示例:

curl --insecure \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Accept: application/json" \
  https://localhost:8080/apis/results.tekton.dev/v1alpha2/parents/-/results/-?filter=data.status.completionTime.getDate()==7

使用tkn-results 的 CEL 过滤表达式

如果您已独立安装 tkn-results CLI 或作为 tkn 的插件安装,您可以使用 --filter=<cel-expression> 标志来过滤Results。请参阅以下示例:

tkn results records list default/results/- --filter="data.metadata.annotations.contains('bar')"

常用过滤器示例

这些示例展示了最常用的过滤表达式,这些表达式对于日常使用非常有用。请记住,并非所有这些过滤器都适用于Results、记录和日志。您必须为正确的资源提供正确的过滤器。

目的过滤表达式
获取 TaskRun/PipelineRun 名称为 hello 的所有记录data.metadata.name == 'hello'
获取属于 PipelineRun 'foo' 的 TaskRun 的所有记录data.metadata.labels['tekton.dev/pipelineRun'] == 'foo'
获取属于 Pipeline 'bar' 的 TaskRun/PipelineRun 的所有记录data.metadata.labels['tekton.dev/pipeline'] == 'bar'
与上述查询相同,但我只想要 PipelineRundata.metadata.labels['tekton.dev/pipeline'] == 'bar' && data_type == 'PIPELINE_RUN'
获取名称以 hello 开头的 TaskRun 的记录data.metadata.name.startsWith('hello')&&dat_type==TASK_RUN
获取所有成功的 TaskRun 的Resultssummary.status == SUCCESS && summary.type == 'TASK_RUN'
获取完成时间超过 5 分钟的 PipelineRun 的记录data.status.completionTime - data.status.startTime > duration('5m') && data_type == 'PIPELINE_RUN'
获取今天完成的运行的记录(假设今天是 7 日)data.status.completionTime.getDate() == 7
获取具有包含 bar 注释的 PipelineRun 的记录data.metadata.annotations.contains('bar') && data_type == 'PIPELINE_RUN'
获取具有包含 bar 注释且名称以 foo 开头的 PipelineRun 的记录data.metadata.annotations.contains('bar') && data.metadata.name.startsWith('foo') && data_type == 'PIPELINE_RUN'
获取包含注释 foobar 的Resultssummary.annotations.contains('foo') && summary.annotations.contains('bar')
获取所有失败的运行的Results!(summary.status == SUCCESS)
获取所有失败的运行的记录!(data.status.conditions[0].status == 'True')
获取具有 3 个或更多任务的 PipelineRun 的所有记录size(data.status.pipelineSpec.tasks) >= 3 && data_type == 'PIPELINE_RUN'

排序

Results API 的参考实现支持使用可选的方向限定符(ascdesc)对Results和记录响应进行排序。

要请求具有特定顺序的对象列表,请在请求中包含 order_by 查询参数。将其传递给要排序的字段名称。可以使用逗号分隔的列表指定多个字段。示例:

  • create_time
  • update_time asc
  • create_time desc, update_time asc

order_by 中支持的字段:

字段名称
create_time
update_time

分页

Results API 的参考实现支持Results、记录和日志的分页。单个页面中的默认对象数量为 50,最大数量为 10000。

要分页响应,请在请求中包含 page_size 查询参数。它必须是介于 0 和 10000 之间的整数值。如果 page_size 小于特定查询可用的总对象数,则响应将包含 NextPageToken。您可以将此值传递给 page_token 查询参数以获取下一页。这两个查询是独立的,可以单独或一起使用。

名称描述
page_size响应中要获取的对象数量。
page_token要获取的页面的令牌。

跨父级读取Results

通过指定 - 作为父名称,可以跨父级读取Results。这对于在没有事先了解可用父级的情况下列出系统中存储的所有Results非常有用。

跨Results读取记录

通过指定 - 作为Results名称部分,或者通过指定 - 作为父名称,可以跨Results读取记录。 (例如,default/results/--/results/-)。这可以用于在不知道确切Results名称的情况下读取和过滤匹配的记录。

指标

API 服务器包括一个 HTTP 服务器,用于公开 gRPC 服务器 Prometheus 指标。默认情况下,服务在端口 :9090 上公开指标。有关指标结构的更多详细信息,请参阅 https://github.com/grpc-ecosystem/go-grpc-prometheus#metrics

健康

API 服务器包括 gRPC 和 REST 端点,用于监控 API 服务器的服务状态以及各个服务的服务状态。

检查状态

# 使用 gRPC 检查 API 服务器的状态
grpcurl --insecure localhost:8080 grpc.health.v1.Health/Check

# 使用 gRPC 检查单个服务的状态
grpcurl --insecure -d '{"service": "tekton.results.v1alpha2.Results"}' localhost:8080 grpc.health.v1.Health/Check

# 使用 REST 检查 API 服务器的状态
curl -k https://localhost:8080/healthz

# 使用 REST 检查单个服务的状态
curl -k https://localhost:8080/healthz?service=tekton.results.v1alpha2.Results