×

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

  • 验证您的Operator项目是否没有语法错误并已正确打包

  • 查看有关改进Operator方法的建议

Red Hat支持的Operator SDK CLI工具版本(包括与Operator项目相关的脚手架和测试工具)已弃用,并计划在未来版本的OpenShift Container Platform中删除。Red Hat将在当前版本生命周期内为此功能提供错误修复和支持,但此功能将不再接收增强功能,并将从未来的OpenShift Container Platform版本中删除。

不建议使用Red Hat支持的Operator SDK版本创建新的Operator项目。拥有现有Operator项目的Operator作者可以使用OpenShift Container Platform 4.17随附的Operator SDK CLI工具版本来维护其项目并创建针对更新版本的OpenShift Container Platform的Operator版本。

以下与Operator项目相关的基本镜像被弃用。这些基本镜像的运行时功能和配置API仍受支持,用于修复错误和解决CVE。

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

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

有关 OpenShift Container Platform 中已弃用或移除的主要功能的最新列表,请参阅 OpenShift Container Platform 发行说明中的已弃用和移除的功能部分。

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

关于 scorecard 工具

虽然 Operator SDK 的 bundle validate 子命令可以验证本地 bundle 目录和远程 bundle 镜像的内容和结构,但您可以使用 scorecard 命令根据配置文件和测试镜像对您的 Operator 运行测试。这些测试是在配置和构建的测试镜像中实现的,这些测试镜像由 scorecard 执行。

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

Scorecard 工作流程
  1. 创建任何相关自定义资源 (CR) 和 Operator 所需的所有资源

  2. 在 Operator 的部署中创建一个代理容器,以记录对 API 服务器的调用并运行测试

  3. 检查 CR 中的参数

Scorecard 测试不对被测试的 Operator 的状态做任何假设。创建 Operator 和 CR 超出了 scorecard 本身的范围。但是,如果测试设计用于创建资源,则 Scorecard 测试可以创建它们所需的任何资源。

scorecard 命令语法
$ operator-sdk scorecard <bundle_dir_or_image> [flags]

Scorecard 需要一个位置参数,用于指定 Operator bundle 的磁盘路径或 bundle 镜像的名称。

有关标志的更多信息,请运行

$ operator-sdk scorecard -h

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.36.1
    entrypoint:
    - scorecard-test
    - basic-check-spec
    labels:
      suite: basic
      test: basic-check-spec-test
  - image: quay.io/operator-framework/scorecard-test:v1.36.1
    entrypoint:
    - scorecard-test
    - olm-bundle-validation
    labels:
      suite: olm
      test: olm-bundle-validation-test

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

配置字段 描述

image

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

entrypoint

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

labels

Scorecard 定义的或自定义标签,用于选择要运行的测试

内置 Scorecard 测试

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

表 1. 基本测试套件
测试 描述 简称

Spec 块存在

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

basic-check-spec-test

表 2. OLM 测试套件
测试 描述 简称

Bundle 验证

此测试验证传递到 scorecard 的 bundle 中找到的 bundle 清单。如果 bundle 内容包含错误,则测试结果输出将包含验证器日志以及验证库的错误消息。

olm-bundle-validation-test

提供的 API 具有验证

此测试验证提供的 CR 的自定义资源定义 (CRD) 是否包含验证部分,以及是否对在 CR 中检测到的每个 specstatus 字段进行了验证。

olm-crds-have-validation-test

拥有的 CRD 已列出资源

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

olm-crds-have-resources-test

具有描述符的 Spec 字段

此测试验证 CR 的 spec 部分中的每个字段在 CSV 中是否都列有相应的描述符。

olm-spec-descriptors-test

具有描述符的状态字段

此测试验证 CR 的 status 部分中的每个字段在 CSV 中是否都列有相应的描述符。

olm-status-descriptors-test

运行 scorecard 工具

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

先决条件
  • 使用 Operator SDK 生成的 Operator 项目

步骤
  1. 为您的 Operator 生成或重新生成 bundle 清单和元数据

    $ make bundle

    此命令会自动将 scorecard 注释添加到您的 bundle 元数据中,scorecard 命令将使用这些注释来运行测试。

  2. 针对 Operator bundle 的磁盘路径或 bundle 镜像的名称运行 scorecard

    $ operator-sdk scorecard <bundle_dir_or_image>

Scorecard 输出

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

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.36.1",
        "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"
          }
        ]
      }
    }
  ]
}
文本输出片段示例
--------------------------------------------------------------------------------
Image:      quay.io/operator-framework/scorecard-test:v1.36.1
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

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

选择测试

通过将 --selector CLI 标志设置为一组标签字符串来选择 Scorecard 测试。如果未提供选择器标志,则运行 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)'

启用并行测试

作为操作员作者,您可以使用记分卡配置文件为测试定义单独的阶段。阶段按配置文件中定义的顺序依次运行。一个阶段包含一系列测试和一个可配置的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.36.1
        labels:
          suite: basic
          test: basic-check-spec-test
      - entrypoint:
        - scorecard-test
        - olm-bundle-validation
        image: quay.io/operator-framework/scorecard-test:v1.36.1
        labels:
          suite: olm
          test: olm-bundle-validation-test
    1 启用并行测试

    并行阶段中的所有测试将同时执行,记分卡将等待所有测试完成后再继续下一个阶段。这可以使您的测试运行速度更快。

自定义记分卡测试

记分卡工具可以运行遵循以下强制约定的自定义测试

  • 测试在容器镜像中实现

  • 测试接受一个入口点,其中包含命令和参数

  • 测试生成v1alpha3 JSON格式的记分卡输出,测试输出中没有多余的日志

  • 测试可以在/bundle的共享挂载点获取捆绑包内容

  • 测试可以使用集群内客户端连接访问 Kubernetes API

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

以下示例显示了一个用 Go 编写的自定义测试镜像

自定义记分卡测试示例
// 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
//
//     https://apache.ac.cn/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},
	}
}