$ operator-sdk scorecard <bundle_dir_or_image> [flags]
作为Operator作者,您可以使用Operator SDK中的scorecard工具执行以下任务:
验证您的Operator项目是否没有语法错误并正确打包
查看有关改进Operator方法的建议
Red Hat支持的Operator SDK CLI工具版本(包括与Operator项目相关的脚手架和测试工具)已弃用,并计划在未来版本的Red Hat OpenShift Service on AWS中删除。Red Hat将在当前发布生命周期内为此功能提供错误修复和支持,但此功能将不再接收增强功能,并将从未来的Red Hat OpenShift Service on AWS版本中删除。 不建议使用Red Hat支持的Operator SDK版本创建新的Operator项目。拥有现有Operator项目的Operator作者可以使用Red Hat OpenShift Service on AWS发布的Operator SDK CLI工具版本来维护其项目并创建针对较新版本的Red Hat OpenShift Service on AWS的Operator版本。 以下与Operator项目相关的基础镜像未被弃用。这些基础镜像的运行时功能和配置API仍然支持错误修复和解决CVE。
有关不受支持的社区维护的Operator SDK版本的信息,请参见Operator SDK (Operator Framework)。 |
虽然Operator SDK的`bundle validate`子命令可以验证本地bundle目录和远程bundle镜像的内容和结构,但您可以使用`scorecard`命令根据配置文件和测试镜像对Operator运行测试。这些测试是在配置和构建的测试镜像中实现的,以便scorecard执行。
scorecard假定它是在可以访问已配置的Kubernetes集群(例如Red Hat OpenShift Service on AWS)的情况下运行的。scorecard在pod内运行每个测试,从中聚合pod日志并将测试结果发送到控制台。scorecard具有内置的基本测试和Operator Lifecycle Manager (OLM)测试,并且还提供了一种执行自定义测试定义的方法。
创建任何相关的自定义资源(CR)和Operator所需的所有资源
在Operator的部署中创建一个代理容器,以记录对API服务器的调用并运行测试
检查CR中的参数
scorecard测试不对被测试的Operator的状态做任何假设。创建Operators和CRs超出了scorecard本身的范围。但是,如果测试设计用于资源创建,则scorecard测试可以创建它们所需的任何资源。
scorecard
命令语法$ operator-sdk scorecard <bundle_dir_or_image> [flags]
scorecard需要一个位置参数,用于指定Operator bundle的磁盘路径或bundle镜像的名称。
有关标志的更多信息,请运行
$ operator-sdk scorecard -h
scorecard工具使用一个配置,允许您配置内部插件以及多个全局配置选项。测试由名为`config.yaml`的配置文件驱动,该文件由`make bundle`命令生成,位于您的`bundle/`目录中。
./bundle
...
└── tests
└── scorecard
└── config.yaml
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配置文件的以下字段定义测试如下:
配置字段 | 描述 |
---|---|
|
实现测试的测试容器镜像名称 |
|
在测试镜像中调用以执行测试的命令和参数 |
|
Scorecard定义的或自定义标签,用于选择要运行的测试 |
scorecard附带预定义的测试,这些测试被安排到套件中:基本测试套件和Operator Lifecycle Manager (OLM)套件。
测试 | 描述 | 简称 |
---|---|---|
Spec Block Exists |
此测试检查在集群中创建的自定义资源(CR),以确保所有CR都具有`spec`块。 |
|
测试 | 描述 | 简称 |
---|---|---|
Bundle Validation |
此测试验证传递到scorecard的bundle中找到的bundle清单。如果bundle内容包含错误,则测试结果输出包括验证器日志以及验证库的错误消息。 |
|
Provided APIs Have Validation |
此测试验证提供的CR的自定义资源定义(CRD)是否包含验证部分,以及是否对在CR中检测到的每个`spec`和`status`字段进行验证。 |
|
已拥有 CRD 资源列表 |
此测试确保通过 |
|
带有描述符的 Spec 字段 |
此测试验证 CRs |
|
带有描述符的 Status 字段 |
此测试验证 CRs |
|
运行init
命令后,Operator SDK 会生成一组默认的 Kustomize 文件。生成的默认bundle/tests/scorecard/config.yaml
文件可以立即用于针对您的 Operator 运行 scorecard 工具,或者您可以根据您的测试规范修改此文件。
使用 Operator SDK 生成的 Operator 项目
为您的 Operator 生成或重新生成 bundle 清单和元数据
$ make bundle
此命令会自动将 scorecard 注释添加到您的 bundle 元数据中,scorecard
命令会使用这些注释来运行测试。
针对 Operator bundle 的本地磁盘路径或 bundle 镜像名称运行 scorecard
$ operator-sdk scorecard <bundle_dir_or_image>
scorecard
命令的--output
标志指定 scorecard 结果的输出格式:text
或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
输出格式规范与 |
Scorecard 测试是通过将--selector
CLI 标志设置为一组标签字符串来选择的。如果未提供选择器标志,则运行 scorecard 配置文件中的所有测试。
测试按顺序运行,测试结果由 scorecard 聚合并写入标准输出或stdout。
例如,要选择单个测试basic-check-spec-test
,请使用--selector
标志指定测试。
$ operator-sdk scorecard <bundle_dir_or_image> \
-o text \
--selector=test=basic-check-spec-test
例如,要选择一组测试olm
,请指定所有 OLM 测试使用的标签。
$ operator-sdk scorecard <bundle_dir_or_image> \
-o text \
--selector=suite=olm
要选择多个测试,请使用以下语法使用selector
标志指定测试名称。
$ operator-sdk scorecard <bundle_dir_or_image> \
-o text \
--selector='test in (basic-check-spec-test,olm-bundle-validation-test)'
作为 Operator 作者,您可以使用 scorecard 配置文件为测试定义单独的阶段。阶段按其在配置文件中定义的顺序依次运行。一个阶段包含一个测试列表和一个可配置的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 | 启用并行测试 |
并行阶段中的所有测试将同时执行,scorecard 将等待所有测试完成后再继续进行下一阶段。这可以使您的测试运行速度更快。
scorecard 工具可以运行遵循这些强制约定 的自定义测试。
测试在容器镜像中实现。
测试接受包含命令和参数的入口点。
测试以 JSON 格式生成v1alpha3
scorecard 输出,测试输出中没有多余的日志。
测试可以在/bundle
的共享挂载点获取 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
//
// http://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},
}
}