5.9. 스코어 카드 툴을 사용하여 Operator 검증

Operator 작성자는 Operator SDK의 스코어 카드 툴을 사용하여 다음 작업을 수행할 수 있습니다.

  • Operator 프로젝트에 구문 오류가 없고 올바르게 패키지되었는지 확인합니다.
  • Operator를 개선할 수 있는 방법에 대한 제안 사항 검토

5.9.1. 스코어 카드 툴 정보

Operator SDK bundle validate 하위 명령은 콘텐츠 및 구조에 대한 로컬 번들 디렉터리 및 원격 번들 이미지를 검증할 수 있지만 scorecard 명령을 사용하면 구성 파일 및 테스트 이미지를 기반으로 Operator에서 테스트를 실행할 수 있습니다. 이러한 테스트는 스코어 카드에 의해 실행되도록 구성된 테스트 이미지 내에서 구현됩니다.

스코어 카드는 OpenShift Container Platform과 같이 구성된 Kubernetes 클러스터에 대한 액세스 권한을 사용하여 실행된다고 가정합니다. 스코어 카드는 각 테스트를 Pod 내에서 실행하며 해당 Pod에서 로그가 집계되고 테스트 결과가 콘솔로 전송됩니다. 스코어 카드에는 기본 테스트 및 OLM(Operator Lifecycle Manager) 테스트가 내장되어 있으며 사용자 정의 테스트 정의를 실행하는 방법도 제공합니다.

스코어 카드 워크플로

  1. 관련 CR(사용자 정의 리소스) 및 Operator에 필요한 모든 리소스를 생성합니다.
  2. Operator 배포에 프록시 컨테이너를 생성하여 API 서버에 대한 호출을 기록하고 테스트를 실행합니다.
  3. CR의 매개변수 검사

스코어 카드 테스트에서는 테스트 중인 Operator의 상태를 가정하지 않습니다. Operator에 대한 Operator 및 CR 생성은 스코어 카드 자체의 범위를 벗어납니다. 그러나 테스트가 리소스 생성을 위해 설계된 경우 필요한 모든 리소스를 생성할 수 있습니다.

scorecard 명령 구문

$ operator-sdk scorecard <bundle_dir_or_image> [flags]

스코어 카드에는 Operator 번들에 대한 디스크상의 경로 또는 번들 이미지 이름에 대한 위치 인수가 필요합니다.

플래그에 대한 자세한 내용을 보려면 다음을 실행합니다.

$ operator-sdk scorecard -h

5.9.2. 스코어 카드 구성

스코어 카드 툴에서는 여러 글로벌 구성 옵션과 내부 플러그인을 구성할 수 있는 구성을 사용합니다. 테스트는 bundle/ 디렉터리에 있는 make bundle 명령으로 생성되는 config.yaml 구성 파일로 구동됩니다.

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

구성 파일은 스코어 카드로 실행할 수 있는 각 테스트를 정의합니다. 스코어 카드 구성 파일의 다음 필드는 다음과 같이 테스트를 정의합니다.

구성 필드Description

image

테스트를 구현하는 컨테이너 이미지 이름 테스트

entrypoint

테스트를 실행하기 위해 테스트 이미지에서 호출되는 명령 및 인수

labels

실행할 테스트를 선택하는 스코어 카드 정의 또는 사용자 정의 라벨

5.9.3. 기본 제공 스코어 카드 테스트

스코어 카드는 도구 모음(기본 테스트 도구 모음 및 OLM(Operator Lifecycle Manager) 도구 모음)으로 준비된 사전 정의 테스트와 함께 제공됩니다.

표 5.16. 기본 테스트 모음

테스트Description짧은 이름

Spec Block Exists

이 테스트에서는 클러스터에서 생성된 모든 CR(사용자 정의 리소스)에 spec 블록이 있는지 확인합니다.

basic-check-spec-test

표 5.17. OLM 테스트 도구 모음

테스트Description짧은 이름

Bundle Validation

이 테스트에서는 스코어 카드로 전달되는 번들에 있는 번들 매니페스트를 검증합니다. 번들 콘텐츠에 오류가 포함된 경우 테스트 결과 출력에 검증기 로그 및 검증 라이브러리의 오류 메시지가 포함됩니다.

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에서 CSV(ClusterServiceVersion)의 owned CRD 섹션에 resources 하위 섹션이 있는지 확인합니다. 테스트에서 resources 섹션에 나열되지 않은 사용된 리소스를 탐지하면 테스트 종료 시 제안 사항에 해당 리소스를 나열합니다. 이 테스트를 통과하기 위해서는 사용자가 초기 코드 생성 후 resources 섹션을 작성해야 합니다.

olm-crds-have-resources-test

Spec Fields With Descriptors

이 테스트에서는 CR spec 섹션의 모든 필드에 CSV에 나열된 해당 설명자가 있는지 확인합니다.

olm-spec-descriptors-test

Status Fields With Descriptors

이 테스트에서는 CR status 섹션의 모든 필드에 CSV에 나열된 해당 설명자가 있는지 확인합니다.

olm-status-descriptors-test

5.9.4. 스코어 카드 툴 실행

기본 Kuryrstomize 파일 세트는 init 명령을 실행한 후 Operator SDK에서 생성합니다. 생성된 기본 bundle/tests/scorecard/config.yaml 파일은 즉시 사용하여 Operator에 대해 스코어 카드 툴을 실행하거나 이 파일을 테스트 사양으로 수정할 수 있습니다.

사전 요구 사항

  • Operator SDK를 사용하여 Operator 프로젝트 생성

프로세스

  1. Operator에 대한 번들 매니페스트 및 메타데이터를 생성하거나 다시 생성합니다.

    $ make bundle

    이 명령을 수행하면 테스트를 실행하는 데 scorecard 명령에서 사용하는 번들 메타데이터에 스코어 카드 주석이 자동으로 추가됩니다.

  2. Operator 번들에 대한 디스크상의 경로 또는 번들 이미지 이름에 대한 스코어 카드를 실행합니다.

    $ operator-sdk scorecard <bundle_dir_or_image>

5.9.5. 스코어 카드 출력

scorecard 명령의 --output 플래그는 스코어 카드 결과 출력 형식을 text 또는 json 중 하나로 지정합니다.

예 5.3. 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.10.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"
          }
        ]
      }
    }
  ]
}

예 5.4. 텍스트 출력 조각의 예

--------------------------------------------------------------------------------
Image:      quay.io/operator-framework/scorecard-test:v1.10.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 유형 레이아웃과 일치합니다.

5.9.6. 테스트 선택

스코어 카드 테스트는 --selector CLI 플래그를 일련의 라벨 문자열로 설정하여 선택합니다. 선택기 플래그를 지정하지 않으면 스코어 카드 구성 파일에 포함된 테스트가 모두 실행됩니다.

테스트는 순차적으로 실행되고 테스트 결과는 스코어 카드에 의해 집계되어 표준 출력 또는 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 작성자는 스코어 카드 구성 파일을 사용하여 테스트에 별도의 단계를 정의할 수 있습니다. 단계는 구성 파일에 정의된 순서에 따라 순차적으로 실행됩니다. 단계에는 테스트 목록과 구성 가능한 parallel 설정이 포함되어 있습니다.

기본적으로 또는 특정 단계에서 명시적으로 parallelfalse로 설정한 경우 단계의 테스트는 구성 파일에 정의된 순서에 따라 순차적으로 실행됩니다. 테스트를 한 번에 하나씩 실행하면 두 테스트가 서로 상호 작용하며 충돌하지 않도록 하는 데 유용합니다.

그러나 테스트를 완전히 격리하도록 설계하면 병렬화할 수 있습니다.

프로세스

  • 격리된 테스트 세트를 병렬로 실행하려면 동일한 단계에 테스트 세트를 포함하고 paralleltrue로 설정합니다.

    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.10.1
        labels:
          suite: basic
          test: basic-check-spec-test
      - entrypoint:
        - scorecard-test
        - olm-bundle-validation
        image: quay.io/operator-framework/scorecard-test:v1.10.1
        labels:
          suite: olm
          test: olm-bundle-validation-test
    1
    병렬 테스트 사용

    병렬 단계의 테스트는 모두 동시에 실행되고 스코어 카드는 테스트가 모두 완료될 때까지 기다린 후 다음 단계를 진행합니다. 이 경우 테스트가 훨씬 빨라질 수 있습니다.

5.9.8. 사용자 정의 스코어 카드 테스트

스코어 카드 툴에서는 다음과 같은 필수 규칙을 따르는 사용자 정의 테스트를 실행할 수 있습니다.

  • 테스트를 컨테이너 이미지 내에서 구현함
  • 테스트에서 명령 및 인수를 포함하는 진입점 허용
  • 테스트에서 테스트 출력과 관련 없는 로그를 기록하지 않고 JSON 형식으로 v1alpha3 스코어 카드 출력 생성
  • 테스트에서 /bundle의 공유 마운트 옵션에 있는 번들 콘텐츠를 가져올 수 있음
  • 테스트에서 클러스터 내 클라이언트 연결을 사용하여 Kubernetes API에 액세스할 수 있음

테스트 이미지가 위 지침을 따르는 경우 다른 프로그래밍 언어로 사용자 정의 테스트를 작성할 수 있습니다.

다음 예제는 Go에서 작성한 사용자 정의 테스트 이미지입니다.

예 5.5. 사용자 정의 스코어 카드 테스트 예제

// 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},
	}
}