sanity

package
v4.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2022 License: Apache-2.0 Imports: 22 Imported by: 1

README

CSI Driver Sanity Tester

This library provides a simple way to ensure that a CSI driver conforms to the CSI specification. There are two ways to leverage this testing framework. For CSI drivers written in Golang, the framework provides a simple API function to call to test the driver. Another way to run the test suite is to use the command line program csi-sanity.

For Golang CSI Drivers

This framework leverages the Ginkgo BDD testing framework to deliver a descriptive test suite for your driver. To test your driver, simply call the API in one of your Golang TestXXX functions. For example:

func TestMyDriver(t *testing.T) {
	// Setup the full driver and its environment
	... setup driver ...
	config := sanity.NewTestConfig()
	// Set configuration options as needed
	cfg.Address = endpoint

	// Now call the test suite
	sanity.Test(t, config)
}

Only one such test function is supported because under the hood a Ginkgo test suite gets constructed and executed by the call.

Alternatively, the tests can also be embedded inside a Ginkgo test suite. In that case it is possible to define multiple tests with different configurations:

var _ = Describe("MyCSIDriver", func () {
	Context("Config A", func () {
		var config &sanity.Config

		BeforeEach(func() {
			//... setup driver and config...
		})

		AfterEach(func() {
			//...tear down driver...
		})

		Describe("CSI sanity", func() {
			sanity.GinkgoTest(config)
		})
	})

	Context("Config B", func () {
		// other configs
	})
})

Command line program

Please see csi-sanity

Documentation

Index

Constants

View Source
const (
	// DefTestVolumeExpand defines the size increment for volume
	// expansion. It can be overriden by setting an
	// Config.TestVolumeExpandSize, which will be taken as absolute
	// value.
	DefTestExpandIncrement int64 = 1 * 1024 * 1024 * 1024

	MaxNameLength int = 128
)

Variables

This section is empty.

Functions

func DescribeSanity

func DescribeSanity(text string, body func(*TestContext)) bool

DescribeSanity must be used instead of the usual Ginkgo Describe to register a test block. The difference is that the body function will be called multiple times with the right context (when setting up a Ginkgo suite or a testing.T test, with the right configuration).

func MakeControllerPublishVolumeReq

func MakeControllerPublishVolumeReq(sc *TestContext, volID, nodeID string) *csi.ControllerPublishVolumeRequest

MakeControllerPublishVolumeReq creates and returns a ControllerPublishVolumeRequest.

func MakeControllerUnpublishVolumeReq

func MakeControllerUnpublishVolumeReq(sc *TestContext, volID, nodeID string) *csi.ControllerUnpublishVolumeRequest

MakeControllerUnpublishVolumeReq creates and returns a ControllerUnpublishVolumeRequest.

func MakeCreateSnapshotReq

func MakeCreateSnapshotReq(sc *TestContext, name, sourceVolumeId string) *csi.CreateSnapshotRequest

func MakeCreateVolumeReq

func MakeCreateVolumeReq(sc *TestContext, name string) *csi.CreateVolumeRequest

func MakeDeleteSnapshotReq

func MakeDeleteSnapshotReq(sc *TestContext, id string) *csi.DeleteSnapshotRequest

func MakeDeleteVolumeReq

func MakeDeleteVolumeReq(sc *TestContext, id string) *csi.DeleteVolumeRequest

func PseudoUUID

func PseudoUUID() string

PseudoUUID returns a unique string generated from random bytes, empty string in case of error.

func Test

func Test(t GinkgoTestingT, config TestConfig)

Test will test the CSI driver at the specified address by setting up a Ginkgo suite and running it.

func TestVolumeExpandSize

func TestVolumeExpandSize(sc *TestContext) int64

func TestVolumeSize

func TestVolumeSize(sc *TestContext) int64

func UniqueString

func UniqueString(prefix string) string

UniqueString returns a unique string by appending a random number. In case of an error, just the prefix is returned, so it alone should already be fairly unique.

func VolumeLifecycle

func VolumeLifecycle(r *Resources, sc *TestContext, count int)

VolumeLifecycle performs Create-Publish-Unpublish-Delete, with optional repeat count to test idempotency.

Types

type CSISecrets

type CSISecrets struct {
	CreateVolumeSecret                         map[string]string `yaml:"CreateVolumeSecret"`
	DeleteVolumeSecret                         map[string]string `yaml:"DeleteVolumeSecret"`
	ControllerPublishVolumeSecret              map[string]string `yaml:"ControllerPublishVolumeSecret"`
	ControllerUnpublishVolumeSecret            map[string]string `yaml:"ControllerUnpublishVolumeSecret"`
	ControllerValidateVolumeCapabilitiesSecret map[string]string `yaml:"ControllerValidateVolumeCapabilitiesSecret"`
	NodeStageVolumeSecret                      map[string]string `yaml:"NodeStageVolumeSecret"`
	NodePublishVolumeSecret                    map[string]string `yaml:"NodePublishVolumeSecret"`
	CreateSnapshotSecret                       map[string]string `yaml:"CreateSnapshotSecret"`
	DeleteSnapshotSecret                       map[string]string `yaml:"DeleteSnapshotSecret"`
	ControllerExpandVolumeSecret               map[string]string `yaml:"ControllerExpandVolumeSecret"`
	ListSnapshotsSecret                        map[string]string `yaml:"ListSnapshotsSecret"`
}

CSISecrets consists of secrets used in CSI credentials.

type DefaultIDGenerator

type DefaultIDGenerator struct {
}

func (DefaultIDGenerator) GenerateInvalidNodeID

func (d DefaultIDGenerator) GenerateInvalidNodeID() string

func (DefaultIDGenerator) GenerateInvalidVolumeID

func (d DefaultIDGenerator) GenerateInvalidVolumeID() string

func (DefaultIDGenerator) GenerateUniqueValidNodeID

func (d DefaultIDGenerator) GenerateUniqueValidNodeID() string

func (DefaultIDGenerator) GenerateUniqueValidVolumeID

func (d DefaultIDGenerator) GenerateUniqueValidVolumeID() string

type IDGenerator

type IDGenerator interface {
	// GenerateUniqueValidVolumeID must generate a unique Volume ID that the CSI
	// Driver considers in valid form
	GenerateUniqueValidVolumeID() string

	// GenerateInvalidVolumeID must output a Volume ID that the CSI Driver MAY
	// consider invalid. Some drivers may not have requirements on IDs in which
	// case this method should output any non-empty ID
	GenerateInvalidVolumeID() string

	// GenerateUniqueValidNodeID must generate a unique Node ID that the CSI
	// Driver considers in valid form
	GenerateUniqueValidNodeID() string

	// GenerateInvalidNodeID must output a Node ID that the CSI Driver MAY
	// consider invalid. Some drivers may not have requirements on IDs in which
	// case this method should output any non-empty ID
	GenerateInvalidNodeID() string
}

IDGenerator generates valid and invalid Volume and Node IDs to be used in tests

type PathKind added in v4.2.0

type PathKind string

Return codes for CheckPath

const (
	PathIsFile     PathKind = "file"
	PathIsDir      PathKind = "directory"
	PathIsNotFound PathKind = "not_found"
	PathIsOther    PathKind = "other"
)

func CheckPath added in v4.2.0

func CheckPath(path string, config *TestConfig) (PathKind, error)

CheckPath takes a path parameter and returns a code indicating whether it's a file, directory, not found, or other. This can be done using a custom command, custom function, or by the defaultCheckPath function. If an error occurs, it returns an empty string along with the error.

func IsPathKind added in v4.2.0

func IsPathKind(in string) (PathKind, error)

IsPathKind validates that the input string matches one of the defined PathKind values above. If successful, it returns the corresponding PathKind type. Otherwise, it returns an error.

type Resources

type Resources struct {
	Context *TestContext
	// ControllerClient is meant for struct-internal usage. It should only be
	// invoked directly if automatic cleanup is not desired and cannot be
	// avoided otherwise.
	csi.ControllerClient
	// NodeClient is meant for struct-internal usage. It should only be invoked
	// directly if automatic cleanup is not desired and cannot be avoided
	// otherwise.
	csi.NodeClient
	// contains filtered or unexported fields
}

Resources keeps track of resources, in particular volumes and snapshots, that need to be freed when testing is done. It implements both ControllerClient and NodeClient and should be used as the only interaction point to either APIs. That way, Resources can ensure that resources are marked for cleanup as necessary. All methods can be called concurrently.

func (*Resources) Cleanup

func (cl *Resources) Cleanup()

Cleanup calls unpublish methods as needed and deletes all managed resources.

func (*Resources) ControllerPublishVolume

ControllerPublishVolume proxies to a Controller service implementation and adds the node ID to the corresponding volume for cleanup.

func (*Resources) CreateSnapshot

CreateSnapshot proxies to a Controller service implementation and registers the snapshot for cleanup.

func (*Resources) CreateVolume

CreateVolume proxies to a Controller service implementation and registers the volume for cleanup.

func (*Resources) DeleteSnapshot

DeleteSnapshot proxies to a Controller service implementation and unregisters the snapshot from cleanup.

func (*Resources) DeleteVolume

DeleteVolume proxies to a Controller service implementation and unregisters the volume from cleanup.

func (*Resources) MustControllerPublishVolume

MustControllerPublishVolume is like ControllerPublishVolume but asserts that the volume was successfully controller-published.

func (*Resources) MustCreateSnapshot

func (cl *Resources) MustCreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) *csi.CreateSnapshotResponse

MustCreateSnapshot is like CreateSnapshot but asserts that the snapshot was successfully created.

func (*Resources) MustCreateSnapshotFromVolumeRequest

func (cl *Resources) MustCreateSnapshotFromVolumeRequest(ctx context.Context, req *csi.CreateVolumeRequest, snapshotName string) (*csi.CreateSnapshotResponse, *csi.CreateVolumeResponse)

MustCreateSnapshotFromVolumeRequest creates a volume from the given CreateVolumeRequest and a snapshot subsequently. It registers the volume and snapshot and asserts that both were created successfully.

func (*Resources) MustCreateVolume

func (cl *Resources) MustCreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) *csi.CreateVolumeResponse

MustCreateVolume is like CreateVolume but asserts that the volume was successfully created.

type TestConfig

type TestConfig struct {
	// TargetPath is the *parent* directory for NodePublishVolumeRequest.target_path.
	// It gets created and removed by csi-sanity.
	TargetPath string

	// StagingPath is the NodeStageVolumeRequest.staging_target_path.
	// It gets created and removed by csi-sanity.
	StagingPath string

	// Address is the gRPC endpoint (e.g. unix:/tmp/csi.sock or
	// dns:///my-machine:9000) of the CSI driver. If ControllerAddress
	// is empty, it must provide both the controller and node service.
	Address string

	// DialOptions specifies the options that are to be used
	// when connecting to Address. The default is grpc.WithInsecure().
	// A dialer will be added for Unix Domain Sockets.
	DialOptions []grpc.DialOption

	// ControllerAddress optionally provides the gRPC endpoint of
	// the controller service.
	ControllerAddress string

	// ControllerDialOptions specifies the options that are to be used
	// for ControllerAddress.
	ControllerDialOptions []grpc.DialOption

	// SecretsFile is the filename of a .yaml file which is used
	// to populate CSISecrets which are then used for calls to the
	// CSI driver.
	SecretsFile string

	TestVolumeSize int64

	// Target size for ExpandVolume requests. If not specified it defaults to TestVolumeSize + 1 GB
	TestVolumeExpandSize      int64
	TestVolumeParametersFile  string
	TestVolumeParameters      map[string]string
	TestNodeVolumeAttachLimit bool
	TestVolumeAccessType      string

	// JUnitFile is used by Test to store test results in JUnit
	// format. When using GinkgoTest, the caller is responsible
	// for configuring the Ginkgo runner.
	JUnitFile string

	// TestSnapshotParametersFile for setting CreateVolumeRequest.Parameters.
	TestSnapshotParametersFile string
	TestSnapshotParameters     map[string]string

	// Callback functions to customize the creation of target and staging
	// directories. Returns the new paths for mount and staging.
	// If not defined, directories are created in the default way at TargetPath
	// and StagingPath on the host.
	//
	// Both functions can replace the suggested path. What the test then uses
	// is the path returned by them.
	//
	// Note that target and staging directory have different
	// semantics in the CSI spec: for NodeStateVolume,
	// CreateTargetDir must create the directory and return the
	// full path to it. For NodePublishVolume, CreateStagingDir
	// must create the *parent* directory of `path` (or some other
	// directory) and return the full path for an entry inside
	// that created directory.
	CreateTargetDir  func(path string) (string, error)
	CreateStagingDir func(path string) (string, error)

	// Callback functions to customize the removal of the target and staging
	// directories.
	// If not defined, directories are removed in the default way at TargetPath
	// and StagingPath on the host.
	//
	// Both functions are passed the actual paths as used during the test.
	//
	// Note that RemoveTargetPath only needs to remove the *parent* of the
	// given path. The CSI driver should have removed the entry at that path
	// already.
	RemoveTargetPath  func(path string) error
	RemoveStagingPath func(path string) error

	// Commands to be executed for customized creation of the target and staging
	// paths. This command must be available on the host where sanity runs. The
	// stdout of the commands are the paths for mount and staging.
	CreateTargetPathCmd  string
	CreateStagingPathCmd string
	// Timeout for the executed commands for path creation.
	CreatePathCmdTimeout time.Duration

	// Commands to be executed for customized removal of the target and staging
	// paths. Thie command must be available on the host where sanity runs.
	RemoveTargetPathCmd  string
	RemoveStagingPathCmd string
	// Timeout for the executed commands for path removal.
	RemovePathCmdTimeout time.Duration

	// IDGen is an interface for callers to provide a
	// generator for valid Volume and Node IDs. Defaults to
	// DefaultIDGenerator.
	IDGen IDGenerator

	// Repeat count for Volume operations to test idempotency requirements.
	// some tests can optionally run repeated variants for those Volume operations
	// that are required to be idempotent, based on this count value.
	// <= 0: skip idempotency tests
	// n > 0: repeat each call n times
	// NewTestConfig() by default enables idempotency testing.
	IdempotentCount int

	// CheckPath is a callback function to check whether the given path exists.
	// If this is not set, then defaultCheckPath will be used instead.
	CheckPath func(path string) (PathKind, error)
	// Command to be executed for a customized way to check a given path.
	CheckPathCmd string
	// Timeout for the executed command to check a given path.
	CheckPathCmdTimeout time.Duration
}

TestConfig provides the configuration for the sanity tests. It must be constructed with NewTestConfig to initialize it with sane defaults. The user of the sanity package can then override values before passing the instance to [Ginkgo]Test and/or (when using GinkgoTest) in a BeforeEach. For example, the BeforeEach could set up the CSI driver and then set the Address field differently for each test.

func NewTestConfig

func NewTestConfig() TestConfig

NewTestConfig returns a config instance with all values set to their defaults.

type TestContext

type TestContext struct {
	Config         *TestConfig
	Conn           *grpc.ClientConn
	ControllerConn *grpc.ClientConn
	Secrets        *CSISecrets

	// Target and staging paths derived from the sanity config.
	TargetPath  string
	StagingPath string
	// contains filtered or unexported fields
}

TestContext gets initialized by the sanity package before each test runs. It holds the variables that each test can depend on.

func GinkgoTest

func GinkgoTest(config *TestConfig) *TestContext

GinkoTest is another entry point for sanity testing: instead of directly running tests like Test does, it merely registers the tests. This can be used to embed sanity testing in a custom Ginkgo test suite. The pointer to the configuration is merely stored by GinkgoTest for use when the tests run. Therefore its content can still be modified in a BeforeEach. The sanity package itself treats it as read-only.

func NewTestContext

func NewTestContext(config *TestConfig) *TestContext

NewContext sets up sanity testing with a config supplied by the user of the sanity package. Ownership of that config is shared between the sanity package and the caller.

func (*TestContext) Finalize

func (sc *TestContext) Finalize()

Finalize frees any resources that might be still cached in the context. It should be called after running all tests.

func (*TestContext) Setup

func (sc *TestContext) Setup()

Setup must be invoked before each test. It initialize per-test variables in the context.

func (*TestContext) Teardown

func (sc *TestContext) Teardown()

Teardown must be called after each test. It frees resources allocated by Setup.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL