搜索

5.9. 使用 scorecard 工具验证 Operator

download PDF

作为 Operator 作者,您可以使用 Operator SDK 中的 scorecard 工具来执行以下任务:

  • 验证您的 Operator 项目没有语法错误,并正确打包
  • 查看有关如何改进 Operator 的建议
重要

红帽支持的 Operator SDK CLI 工具版本,包括 Operator 项目的相关构建和测试工具已被弃用,计划在以后的 OpenShift Dedicated 发行版本中删除。红帽将在当前发行生命周期中提供对这个功能的程序错误修复和支持,但此功能将不再获得改进,并将在以后的 OpenShift Dedicated 版本中删除。

对于创建新 Operator 项目,不建议使用红帽支持的 Operator SDK 版本。现有 Operator 项目的 Operator 作者可使用 OpenShift Dedicated 4 发布的 Operator SDK CLI 工具版本来维护其项目,并创建针对较新版本的 OpenShift Dedicated 的 Operator 发行版本。

以下与 Operator 项目相关的基础镜像 没有被弃用。这些基础镜像的运行时功能和配置 API 仍然会有程序错误修复和并提供对相关 CVE 的解决方案。

  • 基于 Ansible 的 Operator 项目的基础镜像
  • 基于 Helm 的 Operator 项目的基础镜像

有关 Operator SDK 不支持的、社区维护版本的信息,请参阅 Operator SDK (Operator Framework)

5.9.1. 关于 scorecard 工具

虽然 Operator SDK bundle validate 子命令可为内容和结构验证本地捆绑包目录和远程捆绑包镜像,但您可以使用 scorecard 命令基于配置文件和测试镜像对 Operator 运行测试。这些测试在由 scorecard 配置并组成执行的测试镜像中实施。

Scorecard 假设它是在可以访问已配置的 Kubernetes 集群(如 OpenShift Dedicated.)的情况下运行的。Scorecard 在 pod 中运行每个测试,从中聚合 pod 日志并将测试结果发送到控制台。Scorecard 内置了基本测试和 Operator Lifecycle Manager(OLM)测试,同时还提供了执行自定义测试定义的方法。

Scorecard 工作流

  1. 创建任何相关的自定义资源(CR)和 Operator 所需的所有资源
  2. 在 Operator 部署中创建代理容器,记录对 API 服务器的调用并运行测试
  3. 检查 CR 中的参数

Scorecard 测试不会假定要测试的 Operator 状态。为 Operator 创建 Operator 和 CR 超出了 scorecard 本身的范围。但是,如果测试是为创建资源而设计的,则 scorecard 测试可以创建其所需的任何资源。

scorecard 命令语法

$ operator-sdk scorecard <bundle_dir_or_image> [flags]

Scorecard 需要一个位置参数,它是指向 Operator 捆绑包的磁盘路径或捆绑包镜像的名称。

如需有关标记的更多信息,请运行:

$ operator-sdk scorecard -h

5.9.2. Scorecard 配置

Scorecard 工具使用一个配置来供您配置内部插件以及几个全局配置选项。测试是由名为 config.yaml 的配置文件驱动的,该文件由 make bundle 命令生成,位于 bundle/ 目录中:

./bundle
...
└── tests
    └── scorecard
        └── config.yaml

Scorecard 配置文件示例

kind: Configuration
apiversion: scorecard.operatorframework.io/v1alpha3
metadata:
  name: config
stages:
- parallel: true
  tests:
  - image: quay.io/operator-framework/scorecard-test:v1.31.0
    entrypoint:
    - scorecard-test
    - basic-check-spec
    labels:
      suite: basic
      test: basic-check-spec-test
  - image: quay.io/operator-framework/scorecard-test:v1.31.0
    entrypoint:
    - scorecard-test
    - olm-bundle-validation
    labels:
      suite: olm
      test: olm-bundle-validation-test

配置文件定义 scorecard 可执行的每个测试。Scorecard 配置文件的以下字段定义测试,如下所示:

配置字段描述

image

测试实现测试的容器镜像名称

entrypoint

测试镜像中调用的命令和参数来执行测试

labels

选择要运行的测试的 scorecard 定义或自定义标签

5.9.3. 内置 scorecard 测试

Scorecard 附带预定义的测试,这些测试被放在套件中:基本测试套件和 Operator Lifecycle Manager(OLM)套件。

表 5.20. 基本测试套件
测试描述短名称

Spec Block Exists

此测试会检查集群中创建的自定义资源(CR)以确保所有 CR 都有一个 spec 块。

basic-check-spec-test

表 5.21. OLM 测试套件
测试描述短名称

捆绑包验证

此测试会验证传递给 scorecard 的捆绑包中的捆绑包清单。如果捆绑包内容包含错误,那么测试结果输出中将包括验证器日志以及验证库中的错误消息。

olm-bundle-validation-test

Provided APIs Have Validation

此测试会验证提供的 CR 的自定义资源定义(CRD)是否包含一个验证部分,并且 CR 中检测到的每个 specstatus 字段是否已验证。

olm-crds-have-validation-test

Owned CRDs Have Resources Listed

此测试确保通过 cr-manifest 选项提供的每个 CR 的 CRD 在 ClusterServiceVersion(CSV)的 owned CRDs 部分中有一个 resources 子部分。如果测试检测到未在 resources 部分中列出的已使用资源,它会在测试结束时将它们列在建议中。为这个测试通过初始代码生成后,用户需要填写 resources 部分。

olm-crds-have-resources-test

Spec Fields With Descriptors

此测试会验证 CRs spec 部分中的每一个字段是否都在 CSV 中列出对应的描述符。

olm-spec-descriptors-test

Status Fields With Descriptors

此测试会验证 CRs status 部分中的每一个字段是否都在 CSV 中列出对应的描述符。

olm-status-descriptors-test

5.9.4. 运行 scorecard 工具

Operator SDK 在运行 init 命令后生成一组默认 Kustomize 文件。生成的默认 bundle/tests/scorecard/config.yaml 文件可立即用于针对 Operator 运行 scorecard 工具,或者您可以根据测试规格修改该文件。

先决条件

  • 使用 Operator SDK 生成的 operator 项目

流程

  1. 为 Operator 生成或重新生成捆绑包清单和元数据:

    $ make bundle

    此命令自动将 scorecard 注解添加到捆绑包元数据中,由 scorecard 命令用来运行测试。

  2. 针对 Operator 捆绑包的磁盘路径或捆绑包镜像的名称运行 scorecard:

    $ operator-sdk scorecard <bundle_dir_or_image>

5.9.5. Scorecard 输出

scorecard 命令的 --output 标志指定 scorecard 结果输出格式: textjson

例 5.7. JSON 输出片断示例

{
  "apiVersion": "scorecard.operatorframework.io/v1alpha3",
  "kind": "TestList",
  "items": [
    {
      "kind": "Test",
      "apiVersion": "scorecard.operatorframework.io/v1alpha3",
      "spec": {
        "image": "quay.io/operator-framework/scorecard-test:v1.31.0",
        "entrypoint": [
          "scorecard-test",
          "olm-bundle-validation"
        ],
        "labels": {
          "suite": "olm",
          "test": "olm-bundle-validation-test"
        }
      },
      "status": {
        "results": [
          {
            "name": "olm-bundle-validation",
            "log": "time=\"2020-06-10T19:02:49Z\" level=debug msg=\"Found manifests directory\" name=bundle-test\ntime=\"2020-06-10T19:02:49Z\" level=debug msg=\"Found metadata directory\" name=bundle-test\ntime=\"2020-06-10T19:02:49Z\" level=debug msg=\"Getting mediaType info from manifests directory\" name=bundle-test\ntime=\"2020-06-10T19:02:49Z\" level=info msg=\"Found annotations file\" name=bundle-test\ntime=\"2020-06-10T19:02:49Z\" level=info msg=\"Could not find optional dependencies file\" name=bundle-test\n",
            "state": "pass"
          }
        ]
      }
    }
  ]
}

例 5.8. 文本输出片段示例

--------------------------------------------------------------------------------
Image:      quay.io/operator-framework/scorecard-test:v1.31.0
Entrypoint: [scorecard-test olm-bundle-validation]
Labels:
	"suite":"olm"
	"test":"olm-bundle-validation-test"
Results:
	Name: olm-bundle-validation
	State: pass
	Log:
		time="2020-07-15T03:19:02Z" level=debug msg="Found manifests directory" name=bundle-test
		time="2020-07-15T03:19:02Z" level=debug msg="Found metadata directory" name=bundle-test
		time="2020-07-15T03:19:02Z" level=debug msg="Getting mediaType info from manifests directory" name=bundle-test
		time="2020-07-15T03:19:02Z" level=info msg="Found annotations file" name=bundle-test
		time="2020-07-15T03:19:02Z" level=info msg="Could not find optional dependencies file" name=bundle-test
注意

输出格式 spec 与 Test 类型布局匹配。

5.9.6. 选择测试

Scorecard 测试通过将 --selector CLI 标志设置为一组标签字符串来选择。如果没有提供选择器标志,则运行 scorecard 配置文件中的所有测试。

测试通过 scorecard 聚合并写入标准输出或 stdout 以序列方式运行。

流程

  1. 要选择单个测试(如 basic-check-spec-test),使用 --selector 标志来指定测试:

    $ operator-sdk scorecard <bundle_dir_or_image> \
        -o text \
        --selector=test=basic-check-spec-test
  2. 要选择一组测试(如 olm ),请指定所有 OLM 测试使用的标签:

    $ operator-sdk scorecard <bundle_dir_or_image> \
        -o text \
        --selector=suite=olm
  3. 要选择多个测试,按照以下语法使用 selector 标记指定测试名称:

    $ operator-sdk scorecard <bundle_dir_or_image> \
        -o text \
        --selector='test in (basic-check-spec-test,olm-bundle-validation-test)'

5.9.7. 启用并行测试

作为 Operator 作者,您可以使用 scorecard 配置文件为测试定义独立阶段。阶段会根据配置文件中定义的顺序按照顺序运行。一个阶段(stage)包含测试列表以及一个可配置的 parallel 设置。

默认情况,或当阶段把 parallel 明确设置为 false 时,阶段中的测试会按配置文件中定义的顺序运行。每次只运行一个测试有助于保证两个测试间不会相互交互和冲突。

但是,如果测试被设计为完全隔离,则可以实现并行化。

流程

  • 要并行运行一组隔离测试,在同一个阶段中包括它们,并把 parallel 设置为 true

    apiVersion: scorecard.operatorframework.io/v1alpha3
    kind: Configuration
    metadata:
      name: config
    stages:
    - parallel: true 1
      tests:
      - entrypoint:
        - scorecard-test
        - basic-check-spec
        image: quay.io/operator-framework/scorecard-test:v1.31.0
        labels:
          suite: basic
          test: basic-check-spec-test
      - entrypoint:
        - scorecard-test
        - olm-bundle-validation
        image: quay.io/operator-framework/scorecard-test:v1.31.0
        labels:
          suite: olm
          test: olm-bundle-validation-test
    1
    启用并行测试

    所有并行阶段中的测试都会同时执行,scorecard 会在进入下一阶段前等待所有测试完成。这使得测试可以更快地运行。

5.9.8. 自定义 scorecard 测试

scorecard 工具可按照以下强制约定运行自定义测试:

  • 测试在容器镜像内实施
  • 测试可以接受包含命令和参数的入口点
  • 测试以 JSON 格式生成 v1alpha3 scorecard 输出,在测试输出中没有无关的日志信息
  • 测试可在 /bundle 的共享挂载点获取捆绑包内容
  • 测试可以使用集群内客户端连接访问 Kubernetes API

如果测试镜像遵循上述指南,则可以使用其他编程语言编写自定义测试。

以下示例显示了在 Go 中写入的自定义测试镜像:

例 5.9. 自定义 scorecard 测试示例

// Copyright 2020 The Operator-SDK Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"

	scapiv1alpha3 "github.com/operator-framework/api/pkg/apis/scorecard/v1alpha3"
	apimanifests "github.com/operator-framework/api/pkg/manifests"
)

// This is the custom scorecard test example binary
// As with the Redhat scorecard test image, the bundle that is under
// test is expected to be mounted so that tests can inspect the
// bundle contents as part of their test implementations.
// The actual test is to be run is named and that name is passed
// as an argument to this binary.  This argument mechanism allows
// this binary to run various tests all from within a single
// test image.

const PodBundleRoot = "/bundle"

func main() {
	entrypoint := os.Args[1:]
	if len(entrypoint) == 0 {
		log.Fatal("Test name argument is required")
	}

	// Read the pod's untar'd bundle from a well-known path.
	cfg, err := apimanifests.GetBundleFromDir(PodBundleRoot)
	if err != nil {
		log.Fatal(err.Error())
	}

	var result scapiv1alpha3.TestStatus

	// Names of the custom tests which would be passed in the
	// `operator-sdk` command.
	switch entrypoint[0] {
	case CustomTest1Name:
		result = CustomTest1(cfg)
	case CustomTest2Name:
		result = CustomTest2(cfg)
	default:
		result = printValidTests()
	}

	// Convert scapiv1alpha3.TestResult to json.
	prettyJSON, err := json.MarshalIndent(result, "", "    ")
	if err != nil {
		log.Fatal("Failed to generate json", err)
	}
	fmt.Printf("%s\n", string(prettyJSON))

}

// printValidTests will print out full list of test names to give a hint to the end user on what the valid tests are.
func printValidTests() scapiv1alpha3.TestStatus {
	result := scapiv1alpha3.TestResult{}
	result.State = scapiv1alpha3.FailState
	result.Errors = make([]string, 0)
	result.Suggestions = make([]string, 0)

	str := fmt.Sprintf("Valid tests for this image include: %s %s",
		CustomTest1Name,
		CustomTest2Name)
	result.Errors = append(result.Errors, str)
	return scapiv1alpha3.TestStatus{
		Results: []scapiv1alpha3.TestResult{result},
	}
}

const (
	CustomTest1Name = "customtest1"
	CustomTest2Name = "customtest2"
)

// Define any operator specific custom tests here.
// CustomTest1 and CustomTest2 are example test functions. Relevant operator specific
// test logic is to be implemented in similarly.

func CustomTest1(bundle *apimanifests.Bundle) scapiv1alpha3.TestStatus {
	r := scapiv1alpha3.TestResult{}
	r.Name = CustomTest1Name
	r.State = scapiv1alpha3.PassState
	r.Errors = make([]string, 0)
	r.Suggestions = make([]string, 0)
	almExamples := bundle.CSV.GetAnnotations()["alm-examples"]
	if almExamples == "" {
		fmt.Println("no alm-examples in the bundle CSV")
	}

	return wrapResult(r)
}

func CustomTest2(bundle *apimanifests.Bundle) scapiv1alpha3.TestStatus {
	r := scapiv1alpha3.TestResult{}
	r.Name = CustomTest2Name
	r.State = scapiv1alpha3.PassState
	r.Errors = make([]string, 0)
	r.Suggestions = make([]string, 0)
	almExamples := bundle.CSV.GetAnnotations()["alm-examples"]
	if almExamples == "" {
		fmt.Println("no alm-examples in the bundle CSV")
	}
	return wrapResult(r)
}

func wrapResult(r scapiv1alpha3.TestResult) scapiv1alpha3.TestStatus {
	return scapiv1alpha3.TestStatus{
		Results: []scapiv1alpha3.TestResult{r},
	}
}
Red Hat logoGithubRedditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

© 2024 Red Hat, Inc.