mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-19 15:24:26 +01:00
runtime: move all code to src/runtime
To prepare for merging into kata-containers repository. Signed-off-by: Peng Tao <bergwolf@hyper.sh>
This commit is contained in:
178
src/runtime/pkg/katatestutils/README.md
Normal file
178
src/runtime/pkg/katatestutils/README.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# Kata test utilities
|
||||
|
||||
* [Test Constraints](#test-constraints)
|
||||
* [Usage](#usage)
|
||||
* [Displaying the `TestConstraint`](#displaying-the-testconstraint)
|
||||
* [Associating an issue with a constraint](#associating-an-issue-with-a-constraint)
|
||||
* [Examples](#examples)
|
||||
* [Skip tests based on user](#skip-tests-based-on-user)
|
||||
* [Skip tests based on distro](#skip-tests-based-on-distro)
|
||||
* [Skip tests based on kernel version](#skip-tests-based-on-kernel-version)
|
||||
* [Full details](#full-details)
|
||||
|
||||
This package provides a small set of test utilities. See the
|
||||
[GoDoc](https://godoc.org/github.com/kata-containers/runtime/pkg/katatestutils)
|
||||
for full details.
|
||||
|
||||
## Test Constraints
|
||||
|
||||
This package provides helper functions that accept user-specified constraints
|
||||
that allow you to skip tests.
|
||||
|
||||
### Usage
|
||||
|
||||
Create a `TestConstraint` object using the `NewTestConstraint()` constructor.
|
||||
This takes a single boolean parameter that specifies if debug output is generated.
|
||||
|
||||
In each test that has particular test constraints, call the `NotValid()`
|
||||
method on the `TestConstraint` object, passing one or more constraints that
|
||||
you want to be valid.
|
||||
|
||||
The `NotValid()` function returns `true` if any of the specified constraints
|
||||
are not available. This allows for a more natural way to code an arbitrarily
|
||||
complex test skip as shown in the following example.
|
||||
|
||||
The main object is created in the `init()` function to make it available for
|
||||
all tests:
|
||||
|
||||
```go
|
||||
|
||||
import ktu "katatestutils"
|
||||
|
||||
var tc ktu.TestConstraint
|
||||
|
||||
func init() {
|
||||
tc = NewTestConstraint(true)
|
||||
}
|
||||
|
||||
func TestFoo(t *testing.T) {
|
||||
|
||||
// Specify one or more constraint functions. If not satisfied, the test
|
||||
// will be skipped.
|
||||
if tc.NotValid(...) {
|
||||
t.Skip("skipping test")
|
||||
}
|
||||
|
||||
// Test code ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Displaying the `TestConstraint`
|
||||
|
||||
Note that you could add the `TestConstraint` object to the `Skip()` call as it
|
||||
will provide details of why the skip occurred:
|
||||
|
||||
```go
|
||||
if tc.NotValid(...) {
|
||||
t.Skipf("skipping test as requirements not met: %v", tc)
|
||||
}
|
||||
```
|
||||
|
||||
#### Associating an issue with a constraint
|
||||
|
||||
You can add a constraint which specifies an issue URL for the skip. No
|
||||
checking is performed on the issue but if specified, it will be added to the
|
||||
`TestConstraint` and recorded in error messages and when that object is
|
||||
displayed:
|
||||
|
||||
```go
|
||||
if tc.NotValid(WithIssue("https://github.com/kata-containers/runtime/issues/1586"), ...) {
|
||||
t.Skipf("skipping test as requirements not met: %v", tc)
|
||||
}
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
#### Skip tests based on user
|
||||
|
||||
Use the `NeedRoot()` constraint to skip a test unless running as `root`:
|
||||
|
||||
```go
|
||||
func TestOnlyRunWhenRoot(t *testing.T) {
|
||||
|
||||
if tc.NotValid(ktu.NeedRoot()) {
|
||||
t.Skip("skipping test as not running as root user")
|
||||
}
|
||||
|
||||
// Test code to run as root user ...
|
||||
}
|
||||
```
|
||||
|
||||
Use the `NeedNonRoot()` constraint to skip a test unless running as a
|
||||
non-`root` user:
|
||||
|
||||
```go
|
||||
func TestOnlyRunWhenNotRoot(t *testing.T) {
|
||||
|
||||
if tc.NotValid(ktu.NeedNonRoot()) {
|
||||
t.Skip("skipping test as running as root user")
|
||||
}
|
||||
|
||||
// Test code to run as non-root user ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Skip tests based on distro
|
||||
|
||||
Use the `NeedDistro()` constraint to skip a test unless running on a
|
||||
particular Linux distribution:
|
||||
|
||||
```go
|
||||
func TestOnlyRunOnUbuntu(t *testing.T) {
|
||||
|
||||
if tc.NotValid(ktu.NeedDistro("ubuntu")) {
|
||||
t.Skip("skipping test as not running on ubuntu")
|
||||
}
|
||||
|
||||
// Test code to run on Ubuntu only ...
|
||||
}
|
||||
```
|
||||
|
||||
Use the `NeedDistroNotEquals()` constraint to skip a test unless running
|
||||
on a Linux distribution other than the one specified:
|
||||
|
||||
```go
|
||||
func TestDontRunOnFedora(t *testing.T) {
|
||||
|
||||
if tc.NotValid(ktu.NeedDistroNotEquals("fedora")) {
|
||||
t.Skip("skipping test as running on fedora")
|
||||
}
|
||||
|
||||
// Test code to run on any distro apart from Fedora ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Skip tests based on kernel version
|
||||
|
||||
Use the `NeedKernelVersionGE()` constraint to skip a test unless running on a
|
||||
system with at least the specified kernel version:
|
||||
|
||||
```go
|
||||
func TestNewKernelVersion(t *testing.T) {
|
||||
|
||||
if tc.NotValid(ktu.NeedKernelVersionGE("5.0.10")) {
|
||||
t.Skip("skipping test as kernel is too old")
|
||||
}
|
||||
|
||||
// Test code to run on specified kernel version (or newer) ...
|
||||
}
|
||||
```
|
||||
|
||||
Use the `NeedKernelVersionLT()` constraint to skip a test unless running on a
|
||||
system whose kernel is older than the specified kernel version:
|
||||
|
||||
```go
|
||||
func TestOldKernelVersion(t *testing.T) {
|
||||
|
||||
if tc.NotValid(ktu.NeedKernelVersionLT("4.14.114")) {
|
||||
t.Skip("skipping test as kernel is too new")
|
||||
}
|
||||
|
||||
// Test code to run on specified kernel version (or newer) ...
|
||||
}
|
||||
```
|
||||
|
||||
### Full details
|
||||
|
||||
The public API is shown in [`constraints_api.go`](constraints_api.go) or
|
||||
the [GoDoc](https://godoc.org/github.com/kata-containers/runtime/pkg/katatestutils).
|
||||
484
src/runtime/pkg/katatestutils/constraints.go
Normal file
484
src/runtime/pkg/katatestutils/constraints.go
Normal file
@@ -0,0 +1,484 @@
|
||||
// Copyright (c) 2019 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package katatestutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/blang/semver"
|
||||
)
|
||||
|
||||
const (
|
||||
TestDisabledNeedRoot = "Test disabled as requires root user"
|
||||
TestDisabledNeedNonRoot = "Test disabled as requires non-root user"
|
||||
|
||||
// See https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
osRelease = "/etc/os-release"
|
||||
osReleaseAlternative = "/usr/lib/os-release"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnknownDistroName = errors.New("unknown distro name")
|
||||
errUnknownDistroVersion = errors.New("unknown distro version")
|
||||
errInvalidOpForConstraint = errors.New("invalid operator for constraint type")
|
||||
)
|
||||
|
||||
// String converts the operator to a human-readable value.
|
||||
func (o Operator) String() (s string) {
|
||||
switch o {
|
||||
case eqOperator:
|
||||
s = "=="
|
||||
case geOperator:
|
||||
s = ">="
|
||||
case gtOperator:
|
||||
s = ">"
|
||||
case leOperator:
|
||||
s = "<="
|
||||
case ltOperator:
|
||||
s = "<"
|
||||
case neOperator:
|
||||
s = "!="
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Result is the outcome of a Constraint test
|
||||
type Result struct {
|
||||
// Details of the constraint
|
||||
// (human-readable result of testing for a Constraint).
|
||||
Description string
|
||||
|
||||
// true if constraint was valid
|
||||
Success bool
|
||||
}
|
||||
|
||||
// GetFileContents return the file contents as a string.
|
||||
func getFileContents(file string) (string, error) {
|
||||
bytes, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func getKernelVersion() (string, error) {
|
||||
const procVersion = "/proc/version"
|
||||
|
||||
contents, err := getFileContents(procVersion)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fields := strings.Fields(contents)
|
||||
l := len(fields)
|
||||
if l < 3 {
|
||||
return "", fmt.Errorf("unexpected contents in %v", procVersion)
|
||||
}
|
||||
|
||||
return fixKernelVersion(fields[2]), nil
|
||||
}
|
||||
|
||||
// getDistroDetails returns the distributions name and version string.
|
||||
// If it is not possible to determine both values an error is
|
||||
// returned.
|
||||
func getDistroDetails() (name, version string, err error) {
|
||||
files := []string{osRelease, osReleaseAlternative}
|
||||
name = ""
|
||||
version = ""
|
||||
|
||||
for _, file := range files {
|
||||
contents, err := getFileContents(file)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
lines := strings.Split(contents, "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "ID=") && name == "" {
|
||||
fields := strings.Split(line, "=")
|
||||
name = strings.Trim(fields[1], `"`)
|
||||
name = strings.ToLower(name)
|
||||
} else if strings.HasPrefix(line, "VERSION_ID=") && version == "" {
|
||||
fields := strings.Split(line, "=")
|
||||
version = strings.Trim(fields[1], `"`)
|
||||
version = strings.ToLower(version)
|
||||
}
|
||||
}
|
||||
|
||||
if name != "" && version != "" {
|
||||
return name, version, nil
|
||||
}
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return "", "", errUnknownDistroName
|
||||
}
|
||||
|
||||
if version == "" {
|
||||
return "", "", errUnknownDistroVersion
|
||||
}
|
||||
|
||||
return name, version, nil
|
||||
}
|
||||
|
||||
// fixKernelVersion replaces underscores with dashes in a version string.
|
||||
// This change is primarily for Fedora, RHEL and CentOS version numbers which
|
||||
// can contain underscores. By replacing them with dashes, a valid semantic
|
||||
// version string is created.
|
||||
//
|
||||
// Examples of actual kernel versions which can be made into valid semver
|
||||
// format by calling this function:
|
||||
//
|
||||
// centos: 3.10.0-957.12.1.el7.x86_64
|
||||
// fedora: 5.0.9-200.fc29.x86_64
|
||||
//
|
||||
func fixKernelVersion(version string) string {
|
||||
return strings.Replace(version, "_", "-", -1)
|
||||
}
|
||||
|
||||
// handleDistroName checks that the current distro is compatible with
|
||||
// the constraint specified by the arguments.
|
||||
func (tc *TestConstraint) handleDistroName(name string, op Operator) (result Result, err error) {
|
||||
if name == "" {
|
||||
return Result{}, fmt.Errorf("distro name cannot be blank")
|
||||
}
|
||||
|
||||
name = strings.ToLower(name)
|
||||
|
||||
var success bool
|
||||
|
||||
switch op {
|
||||
case eqOperator:
|
||||
success = name == tc.DistroName
|
||||
case neOperator:
|
||||
success = name != tc.DistroName
|
||||
default:
|
||||
return Result{}, errInvalidOpForConstraint
|
||||
}
|
||||
|
||||
descr := fmt.Sprintf("need distro %s %q, got distro %q", op, name, tc.DistroName)
|
||||
|
||||
result = Result{
|
||||
Description: descr,
|
||||
Success: success,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// handleDistroVersion checks that the current distro version is compatible with
|
||||
// the constraint specified by the arguments.
|
||||
func (tc *TestConstraint) handleDistroVersion(version string, op Operator) (result Result, err error) {
|
||||
return handleVersionType("distro", tc.DistroVersion, op, version)
|
||||
}
|
||||
|
||||
// handleKernelVersion checks that the current kernel version is compatible with
|
||||
// the constraint specified by the arguments.
|
||||
func (tc *TestConstraint) handleKernelVersion(version string, op Operator) (result Result, err error) {
|
||||
return handleVersionType("kernel", tc.KernelVersion, op, version)
|
||||
}
|
||||
|
||||
// handleVersionType checks that the current and new versions are compatible with
|
||||
// the constraint specified by the arguments. The versionName argument is a
|
||||
// human-readable value to represent the currentVersion.
|
||||
func handleVersionType(versionName, newVersion string, op Operator, currentVersion string) (result Result, err error) {
|
||||
if versionName == "" {
|
||||
return Result{}, fmt.Errorf("version name cannot be blank")
|
||||
}
|
||||
|
||||
if newVersion == "" {
|
||||
return Result{}, fmt.Errorf("new version cannot be blank")
|
||||
}
|
||||
|
||||
if currentVersion == "" {
|
||||
return Result{}, fmt.Errorf("current version cannot be blank")
|
||||
}
|
||||
|
||||
newVersion = strings.ToLower(newVersion)
|
||||
currentVersion = strings.ToLower(currentVersion)
|
||||
|
||||
newVersionElements := len(strings.Split(newVersion, "."))
|
||||
currentVersionElements := len(strings.Split(currentVersion, "."))
|
||||
|
||||
var success bool
|
||||
|
||||
// Determine the type of version string based on the current version
|
||||
switch currentVersionElements {
|
||||
case 1:
|
||||
// A simple integer version number.
|
||||
if newVersionElements != 1 {
|
||||
return Result{}, fmt.Errorf("%s version type (%q) is integer, but specified version (%s) is not",
|
||||
versionName, currentVersion, newVersion)
|
||||
}
|
||||
|
||||
success, err = evalIntVersion(newVersion, op, currentVersion)
|
||||
case 2:
|
||||
// A "floating point" version number in format "a.b".
|
||||
if newVersionElements > 2 {
|
||||
return Result{}, fmt.Errorf("%s version type (%q) is float, but specified version (%s) is not float or int",
|
||||
versionName, currentVersion, newVersion)
|
||||
}
|
||||
|
||||
success, err = evalFloatVersion(newVersion, op, currentVersion)
|
||||
default:
|
||||
// Assumed to be a semver format version string
|
||||
// in format "a.b.c."
|
||||
//
|
||||
// Cannot check specified version here as semver is more
|
||||
// complex - let the eval function detail with it.
|
||||
|
||||
success, err = evalSemverVersion(newVersion, op, currentVersion)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return Result{}, err
|
||||
}
|
||||
|
||||
descr := fmt.Sprintf("need %s version %s %q, got version %q",
|
||||
versionName, op, currentVersion, newVersion)
|
||||
|
||||
result = Result{
|
||||
Description: descr,
|
||||
Success: success,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// evalIntVersion deals with integer version numbers
|
||||
// (in format "a").
|
||||
func evalIntVersion(newVersionStr string, op Operator, currentVersionStr string) (success bool, err error) {
|
||||
newVersion, err := strconv.Atoi(newVersionStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
currentVersion, err := strconv.Atoi(currentVersionStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch op {
|
||||
case eqOperator:
|
||||
success = newVersion == currentVersion
|
||||
case geOperator:
|
||||
success = newVersion >= currentVersion
|
||||
case gtOperator:
|
||||
success = newVersion > currentVersion
|
||||
case leOperator:
|
||||
success = newVersion <= currentVersion
|
||||
case ltOperator:
|
||||
success = newVersion < currentVersion
|
||||
case neOperator:
|
||||
success = newVersion != currentVersion
|
||||
default:
|
||||
return false, errInvalidOpForConstraint
|
||||
}
|
||||
|
||||
return success, err
|
||||
}
|
||||
|
||||
// evalFloatVersion deals with "floating point" version numbers
|
||||
// (in format "a.b").
|
||||
//
|
||||
// Note that (implicitly) the specified version number provided by the user
|
||||
// may in fact be an integer value which will be converted into a float.
|
||||
func evalFloatVersion(newVersionStr string, op Operator, currentVersionStr string) (success bool, err error) {
|
||||
// If this many bits is insufficient to represent a version number, we
|
||||
// have problems...!
|
||||
const bitSize = 32
|
||||
|
||||
newVersion, err := strconv.ParseFloat(newVersionStr, bitSize)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
currentVersion, err := strconv.ParseFloat(currentVersionStr, bitSize)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch op {
|
||||
case eqOperator:
|
||||
success = newVersion == currentVersion
|
||||
case geOperator:
|
||||
success = newVersion >= currentVersion
|
||||
case gtOperator:
|
||||
success = newVersion > currentVersion
|
||||
case leOperator:
|
||||
success = newVersion <= currentVersion
|
||||
case ltOperator:
|
||||
success = newVersion < currentVersion
|
||||
case neOperator:
|
||||
success = newVersion != currentVersion
|
||||
default:
|
||||
return false, errInvalidOpForConstraint
|
||||
}
|
||||
|
||||
return success, err
|
||||
}
|
||||
|
||||
// evalSemverVersion deals with semantic versioning format version strings
|
||||
// (in version "a.b.c").
|
||||
//
|
||||
// See: https://semver.org
|
||||
func evalSemverVersion(newVersionStr string, op Operator, currentVersionStr string) (success bool, err error) {
|
||||
newVersion, err := semver.Make(newVersionStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
currentVersion, err := semver.Make(currentVersionStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch op {
|
||||
case eqOperator:
|
||||
success = newVersion.EQ(currentVersion)
|
||||
case geOperator:
|
||||
success = newVersion.GE(currentVersion)
|
||||
case gtOperator:
|
||||
success = newVersion.GT(currentVersion)
|
||||
case leOperator:
|
||||
success = newVersion.LE(currentVersion)
|
||||
case ltOperator:
|
||||
success = newVersion.LT(currentVersion)
|
||||
case neOperator:
|
||||
success = newVersion.NE(currentVersion)
|
||||
default:
|
||||
return false, errInvalidOpForConstraint
|
||||
}
|
||||
|
||||
return success, err
|
||||
}
|
||||
|
||||
// handleUID checks that the current UID is compatible with the constraint
|
||||
// specified by the arguments.
|
||||
func (tc *TestConstraint) handleUID(uid int, op Operator) (result Result, err error) {
|
||||
if uid < 0 {
|
||||
return Result{}, fmt.Errorf("uid must be >= 0, got %d", uid)
|
||||
}
|
||||
|
||||
var success bool
|
||||
|
||||
switch op {
|
||||
case eqOperator:
|
||||
success = tc.ActualEUID == uid
|
||||
case neOperator:
|
||||
success = tc.ActualEUID != uid
|
||||
default:
|
||||
return Result{}, errInvalidOpForConstraint
|
||||
}
|
||||
|
||||
descr := fmt.Sprintf("need uid %s %d, got euid %d", op, uid, tc.ActualEUID)
|
||||
|
||||
result = Result{
|
||||
Description: descr,
|
||||
Success: success,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// handleResults is the common handler for all constraint types. It deals with
|
||||
// errors found trying to check constraints, stores results and displays
|
||||
// details of valid constraints.
|
||||
func (tc *TestConstraint) handleResults(result Result, err error) {
|
||||
if err != nil {
|
||||
var extra string
|
||||
|
||||
if tc.Issue != "" {
|
||||
extra = fmt.Sprintf(" (issue %s)", tc.Issue)
|
||||
}
|
||||
|
||||
// Display the TestConstraint object as it's may provide
|
||||
// helpful information for the caller.
|
||||
panic(fmt.Sprintf("%+v: failed to check test constraints: error: %s%s\n",
|
||||
tc, err, extra))
|
||||
}
|
||||
|
||||
if !result.Success {
|
||||
tc.Failed = append(tc.Failed, result)
|
||||
} else {
|
||||
tc.Passed = append(tc.Passed, result)
|
||||
}
|
||||
|
||||
if tc.Debug {
|
||||
var outcome string
|
||||
|
||||
if result.Success {
|
||||
outcome = "valid"
|
||||
} else {
|
||||
outcome = "invalid"
|
||||
}
|
||||
|
||||
fmt.Printf("Constraint %s: %s\n", outcome, result.Description)
|
||||
}
|
||||
}
|
||||
|
||||
// constraintValid handles the specified constraint, returning true if the
|
||||
// constraint is valid, else false.
|
||||
func (tc *TestConstraint) constraintValid(fn Constraint) bool {
|
||||
c := Constraints{}
|
||||
|
||||
// Call the constraint function that sets the Constraints values
|
||||
fn(&c)
|
||||
|
||||
if c.Issue != "" {
|
||||
// Just record it
|
||||
tc.Issue = c.Issue
|
||||
}
|
||||
|
||||
if c.UIDSet {
|
||||
result, err := tc.handleUID(c.UID, c.Operator)
|
||||
tc.handleResults(result, err)
|
||||
if !result.Success {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if c.DistroName != "" {
|
||||
result, err := tc.handleDistroName(c.DistroName, c.Operator)
|
||||
tc.handleResults(result, err)
|
||||
if !result.Success {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if c.DistroVersion != "" {
|
||||
result, err := tc.handleDistroVersion(c.DistroVersion, c.Operator)
|
||||
tc.handleResults(result, err)
|
||||
if !result.Success {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if c.KernelVersion != "" {
|
||||
result, err := tc.handleKernelVersion(c.KernelVersion, c.Operator)
|
||||
tc.handleResults(result, err)
|
||||
if !result.Success {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Constraint is valid
|
||||
return true
|
||||
}
|
||||
309
src/runtime/pkg/katatestutils/constraints_api.go
Normal file
309
src/runtime/pkg/katatestutils/constraints_api.go
Normal file
@@ -0,0 +1,309 @@
|
||||
// Copyright (c) 2019 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// This file contains the public API for the test constraints facility.
|
||||
|
||||
package katatestutils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Operator represents an operator to apply to a test constraint value.
|
||||
type Operator int
|
||||
|
||||
const (
|
||||
eqOperator Operator = iota
|
||||
geOperator Operator = iota
|
||||
gtOperator Operator = iota
|
||||
leOperator Operator = iota
|
||||
ltOperator Operator = iota
|
||||
neOperator Operator = iota
|
||||
)
|
||||
|
||||
// Constraints encapsulates all information about a test constraint.
|
||||
type Constraints struct {
|
||||
Issue string
|
||||
|
||||
UID int
|
||||
|
||||
// Not ideal: set when UID needs to be checked. This allows
|
||||
// a test for UID 0 to be detected.
|
||||
UIDSet bool
|
||||
|
||||
// DistroName is the name of a distro in all lower-case letters.
|
||||
DistroName string
|
||||
|
||||
// DistroVersion is the version of the particular distro in string
|
||||
// format. It may contain periods and dashes.
|
||||
DistroVersion string
|
||||
|
||||
// KernelVersion is the version of a particular kernel.
|
||||
KernelVersion string
|
||||
|
||||
// Operator is the operator to apply to one of the constraints.
|
||||
Operator Operator
|
||||
}
|
||||
|
||||
// Constraint is a function that operates on a Constraints object to set
|
||||
// particular values.
|
||||
type Constraint func(c *Constraints)
|
||||
|
||||
// TestConstraint records details about test constraints.
|
||||
type TestConstraint struct {
|
||||
Debug bool
|
||||
|
||||
// Effective user ID of running test
|
||||
ActualEUID int
|
||||
|
||||
DistroName string
|
||||
DistroVersion string
|
||||
KernelVersion string
|
||||
|
||||
// Used to record all passed and failed constraints in
|
||||
// human-readable form.
|
||||
Passed []Result
|
||||
Failed []Result
|
||||
|
||||
// Optionally used to record an issue number that relates to the
|
||||
// constraint.
|
||||
Issue string
|
||||
}
|
||||
|
||||
// NewKataTest creates a new TestConstraint object and is the main interface
|
||||
// to the test constraints feature.
|
||||
func NewTestConstraint(debug bool) TestConstraint {
|
||||
distroName, distroVersion, err := getDistroDetails()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
kernelVersion, err := getKernelVersion()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return TestConstraint{
|
||||
Debug: debug,
|
||||
|
||||
ActualEUID: os.Geteuid(),
|
||||
DistroName: distroName,
|
||||
DistroVersion: distroVersion,
|
||||
KernelVersion: kernelVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// NotValid checks if the specified list of constraints are all valid,
|
||||
// returning true if any _fail_.
|
||||
//
|
||||
// Notes:
|
||||
//
|
||||
// - Constraints are applied in the order specified.
|
||||
// - A constraint type (user, distro) can only be specified once.
|
||||
// - If the function fails to determine whether it can check the constraints,
|
||||
// it will panic. Since this is facility is used for testing, this seems like
|
||||
// the best approach as it unburdens the caller from checking for an error
|
||||
// (which should never be ignored).
|
||||
func (tc *TestConstraint) NotValid(constraints ...Constraint) bool {
|
||||
if len(constraints) == 0 {
|
||||
panic("need atleast one constraint")
|
||||
}
|
||||
|
||||
// Reset in case of a previous call
|
||||
tc.Passed = nil
|
||||
tc.Failed = nil
|
||||
tc.Issue = ""
|
||||
|
||||
for _, c := range constraints {
|
||||
valid := tc.constraintValid(c)
|
||||
if !valid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// NeedUID skips the test unless running as a user with the specified user ID.
|
||||
func NeedUID(uid int, op Operator) Constraint {
|
||||
return func(c *Constraints) {
|
||||
c.Operator = op
|
||||
c.UID = uid
|
||||
c.UIDSet = true
|
||||
}
|
||||
}
|
||||
|
||||
// NeedNonRoot skips the test unless running as root.
|
||||
func NeedRoot() Constraint {
|
||||
return NeedUID(0, eqOperator)
|
||||
}
|
||||
|
||||
// NeedNonRoot skips the test if running as the root user.
|
||||
func NeedNonRoot() Constraint {
|
||||
return NeedUID(0, neOperator)
|
||||
}
|
||||
|
||||
// NeedDistroWithOp skips the test unless the distro constraint specified by
|
||||
// the arguments is true.
|
||||
func NeedDistroWithOp(distro string, op Operator) Constraint {
|
||||
return func(c *Constraints) {
|
||||
c.DistroName = strings.ToLower(distro)
|
||||
c.Operator = op
|
||||
}
|
||||
}
|
||||
|
||||
// NeedDistroEquals will skip the test unless running on the specified distro.
|
||||
func NeedDistroEquals(distro string) Constraint {
|
||||
return NeedDistroWithOp(distro, eqOperator)
|
||||
}
|
||||
|
||||
// NeedDistroNotEquals will skip the test unless run a distro that does not
|
||||
// match the specified name.
|
||||
func NeedDistroNotEquals(distro string) Constraint {
|
||||
return NeedDistroWithOp(distro, neOperator)
|
||||
}
|
||||
|
||||
// NeedDistro will skip the test unless running on the specified distro.
|
||||
func NeedDistro(distro string) Constraint {
|
||||
return NeedDistroEquals(distro)
|
||||
}
|
||||
|
||||
// NeedDistroVersionWithOp skips the test unless the distro version constraint
|
||||
// specified by the arguments is true.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedDistroVersionWithOp(version string, op Operator) Constraint {
|
||||
return func(c *Constraints) {
|
||||
c.DistroVersion = version
|
||||
c.Operator = op
|
||||
}
|
||||
}
|
||||
|
||||
// NeedDistroVersionEquals will skip the test unless the distro version is the
|
||||
// same as the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedDistroVersionEquals(version string) Constraint {
|
||||
return NeedDistroVersionWithOp(version, eqOperator)
|
||||
}
|
||||
|
||||
// NeedDistroVersionNotEquals will skip the test unless the distro version is
|
||||
// different to the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedDistroVersionNotEquals(version string) Constraint {
|
||||
return NeedDistroVersionWithOp(version, neOperator)
|
||||
}
|
||||
|
||||
// NeedDistroVersionLE will skip the test unless the distro version is older
|
||||
// than or the same as the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedDistroVersionLE(version string) Constraint {
|
||||
return NeedDistroVersionWithOp(version, leOperator)
|
||||
}
|
||||
|
||||
// NeedDistroVersionLT will skip the test unless the distro version is older
|
||||
// than the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedDistroVersionLT(version string) Constraint {
|
||||
return NeedDistroVersionWithOp(version, ltOperator)
|
||||
}
|
||||
|
||||
// NeedDistroVersionGE will skip the test unless the distro version is newer
|
||||
// than or the same as the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedDistroVersionGE(version string) Constraint {
|
||||
return NeedDistroVersionWithOp(version, geOperator)
|
||||
}
|
||||
|
||||
// NeedDistroVersionGT will skip the test unless the distro version is newer
|
||||
// than the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedDistroVersionGT(version string) Constraint {
|
||||
return NeedDistroVersionWithOp(version, gtOperator)
|
||||
}
|
||||
|
||||
// NeedDistroVersion will skip the test unless running on the specified
|
||||
// (exact) version of some distro.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedDistroVersion(version string) Constraint {
|
||||
return NeedDistroVersionEquals(version)
|
||||
}
|
||||
|
||||
// NeedKernelVersionWithOp skips the test unless the distro version constraint
|
||||
// specified by the arguments is true.
|
||||
func NeedKernelVersionWithOp(version string, op Operator) Constraint {
|
||||
return func(c *Constraints) {
|
||||
c.KernelVersion = version
|
||||
c.Operator = op
|
||||
}
|
||||
}
|
||||
|
||||
// NeedKernelVersionLT will skip the test unless the distro version is older
|
||||
// than the specified version.
|
||||
func NeedKernelVersionEquals(version string) Constraint {
|
||||
return NeedKernelVersionWithOp(version, eqOperator)
|
||||
}
|
||||
|
||||
// NeedKernelVersionNotEquals will skip the test unless the distro version is
|
||||
// different to the specified version.
|
||||
func NeedKernelVersionNotEquals(version string) Constraint {
|
||||
return NeedKernelVersionWithOp(version, neOperator)
|
||||
}
|
||||
|
||||
// NeedKernelVersionLT will skip the test unless the distro version is older
|
||||
// than the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedKernelVersionLT(version string) Constraint {
|
||||
return NeedKernelVersionWithOp(version, ltOperator)
|
||||
}
|
||||
|
||||
// NeedKernelVersionLE will skip the test unless the distro version is older
|
||||
// than or the same as the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedKernelVersionLE(version string) Constraint {
|
||||
return NeedKernelVersionWithOp(version, leOperator)
|
||||
}
|
||||
|
||||
// NeedKernelVersionGT will skip the test unless the distro version is newer
|
||||
// than the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedKernelVersionGT(version string) Constraint {
|
||||
return NeedKernelVersionWithOp(version, gtOperator)
|
||||
}
|
||||
|
||||
// NeedKernelVersionGE will skip the test unless the distro version is newer
|
||||
// than or the same as the specified version.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedKernelVersionGE(version string) Constraint {
|
||||
return NeedKernelVersionWithOp(version, geOperator)
|
||||
}
|
||||
|
||||
// NeedKernelVersion will skip the test unless running on the specified
|
||||
// (exact) version of some distro.
|
||||
//
|
||||
// Note: distro versions vary in format.
|
||||
func NeedKernelVersion(version string) Constraint {
|
||||
return NeedKernelVersionEquals(version)
|
||||
}
|
||||
|
||||
// WithIssue allows the specification of an issue URL.
|
||||
//
|
||||
// Note that the issue is not checked for validity.
|
||||
func WithIssue(issue string) Constraint {
|
||||
return func(c *Constraints) {
|
||||
c.Issue = issue
|
||||
}
|
||||
}
|
||||
1832
src/runtime/pkg/katatestutils/constraints_test.go
Normal file
1832
src/runtime/pkg/katatestutils/constraints_test.go
Normal file
File diff suppressed because it is too large
Load Diff
92
src/runtime/pkg/katatestutils/utils.go
Normal file
92
src/runtime/pkg/katatestutils/utils.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2018-2019 Intel Corporation
|
||||
// Copyright (c) 2018 HyperHQ Inc.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package katatestutils
|
||||
|
||||
import "strconv"
|
||||
|
||||
type RuntimeConfigOptions struct {
|
||||
Hypervisor string
|
||||
HypervisorPath string
|
||||
DefaultVCPUCount uint32
|
||||
DefaultMaxVCPUCount uint32
|
||||
DefaultMemSize uint32
|
||||
DefaultMsize9p uint32
|
||||
DefaultGuestHookPath string
|
||||
KernelPath string
|
||||
ImagePath string
|
||||
KernelParams string
|
||||
MachineType string
|
||||
ShimPath string
|
||||
ProxyPath string
|
||||
NetmonPath string
|
||||
LogPath string
|
||||
BlockDeviceDriver string
|
||||
AgentTraceMode string
|
||||
AgentTraceType string
|
||||
SharedFS string
|
||||
PCIeRootPort uint32
|
||||
DisableBlock bool
|
||||
EnableIOThreads bool
|
||||
HotplugVFIOOnRootBus bool
|
||||
DisableNewNetNs bool
|
||||
HypervisorDebug bool
|
||||
RuntimeDebug bool
|
||||
RuntimeTrace bool
|
||||
ProxyDebug bool
|
||||
ShimDebug bool
|
||||
NetmonDebug bool
|
||||
AgentDebug bool
|
||||
AgentTrace bool
|
||||
}
|
||||
|
||||
func MakeRuntimeConfigFileData(config RuntimeConfigOptions) string {
|
||||
return `
|
||||
# Runtime configuration file
|
||||
|
||||
[hypervisor.` + config.Hypervisor + `]
|
||||
path = "` + config.HypervisorPath + `"
|
||||
kernel = "` + config.KernelPath + `"
|
||||
block_device_driver = "` + config.BlockDeviceDriver + `"
|
||||
kernel_params = "` + config.KernelParams + `"
|
||||
image = "` + config.ImagePath + `"
|
||||
machine_type = "` + config.MachineType + `"
|
||||
default_vcpus = ` + strconv.FormatUint(uint64(config.DefaultVCPUCount), 10) + `
|
||||
default_maxvcpus = ` + strconv.FormatUint(uint64(config.DefaultMaxVCPUCount), 10) + `
|
||||
default_memory = ` + strconv.FormatUint(uint64(config.DefaultMemSize), 10) + `
|
||||
disable_block_device_use = ` + strconv.FormatBool(config.DisableBlock) + `
|
||||
enable_iothreads = ` + strconv.FormatBool(config.EnableIOThreads) + `
|
||||
hotplug_vfio_on_root_bus = ` + strconv.FormatBool(config.HotplugVFIOOnRootBus) + `
|
||||
pcie_root_port = ` + strconv.FormatUint(uint64(config.PCIeRootPort), 10) + `
|
||||
msize_9p = ` + strconv.FormatUint(uint64(config.DefaultMsize9p), 10) + `
|
||||
enable_debug = ` + strconv.FormatBool(config.HypervisorDebug) + `
|
||||
guest_hook_path = "` + config.DefaultGuestHookPath + `"
|
||||
shared_fs = "` + config.SharedFS + `"
|
||||
virtio_fs_daemon = "/path/to/virtiofsd"
|
||||
|
||||
[proxy.kata]
|
||||
enable_debug = ` + strconv.FormatBool(config.ProxyDebug) + `
|
||||
path = "` + config.ProxyPath + `"
|
||||
|
||||
[shim.kata]
|
||||
path = "` + config.ShimPath + `"
|
||||
enable_debug = ` + strconv.FormatBool(config.ShimDebug) + `
|
||||
|
||||
[agent.kata]
|
||||
enable_debug = ` + strconv.FormatBool(config.AgentDebug) + `
|
||||
enable_tracing = ` + strconv.FormatBool(config.AgentTrace) + `
|
||||
trace_mode = "` + config.AgentTraceMode + `"` + `
|
||||
trace_type = "` + config.AgentTraceType + `"` + `
|
||||
|
||||
[netmon]
|
||||
path = "` + config.NetmonPath + `"
|
||||
enable_debug = ` + strconv.FormatBool(config.NetmonDebug) + `
|
||||
|
||||
[runtime]
|
||||
enable_debug = ` + strconv.FormatBool(config.RuntimeDebug) + `
|
||||
enable_tracing = ` + strconv.FormatBool(config.RuntimeTrace) + `
|
||||
disable_new_netns= ` + strconv.FormatBool(config.DisableNewNetNs)
|
||||
}
|
||||
Reference in New Issue
Block a user