testenv

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: May 5, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package testenv provides a test environment for integration testing with BuildKit. It follows the pattern from Azure/dalec's testenv package, allowing tests to execute Go code directly and inspect container state via the BuildKit gateway client.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CompareLayerCounts

func CompareLayerCounts(
	ctx context.Context,
	c gwclient.Client,
	originalImage string,
	patchedConfigData []byte,
	platform *specs.Platform,
) (int, int, error)

CompareLayerCounts compares layer counts between original and patched images. Returns (originalCount, patchedCount, err). The originalImage reference will be normalized to a fully qualified reference.

func CreateUpdateManifest

func CreateUpdateManifest(osType, osVersion, arch string, packages []PackageUpdate) *unversioned.UpdateManifest

CreateUpdateManifest is a helper to create a simple UpdateManifest for testing. This is useful when you want to test patching specific packages.

Example:

updates := testenv.CreateUpdateManifest("debian", "11", "amd64", []testenv.PackageUpdate{
    {Name: "openssl", InstalledVersion: "1.1.1k-1", FixedVersion: "1.1.1n-0+deb11u5"},
})

func NormalizeImageRef

func NormalizeImageRef(imageRef string) string

NormalizeImageRef normalizes an image reference to its fully qualified form. This is necessary because BuildKit's gateway client requires fully qualified image references (e.g., "docker.io/library/alpine:latest" instead of "alpine:latest") to properly parse the URL when resolving image configs.

Examples:

NormalizeImageRef("alpine:latest") returns "docker.io/library/alpine:latest"
NormalizeImageRef("nginx:1.21") returns "docker.io/library/nginx:1.21"
NormalizeImageRef("ghcr.io/myorg/myimage:v1") returns "ghcr.io/myorg/myimage:v1"

Types

type ImageLayerInfo

type ImageLayerInfo struct {
	// LayerCount is the number of layers in the image.
	LayerCount int

	// DiffIDs are the content-addressable identifiers for each layer.
	DiffIDs []string

	// Platform is the platform of the image.
	Platform *specs.Platform
}

ImageLayerInfo contains information about an image's layers.

func GetOriginalImageLayerCount

func GetOriginalImageLayerCount(ctx context.Context, c gwclient.Client, imageName string, platform *specs.Platform) (*ImageLayerInfo, error)

GetOriginalImageLayerCount returns the number of layers in the original (unpatched) image. This uses the gateway client to resolve the image config and count layers. The imageName will be normalized to a fully qualified reference.

type LayerCountTest

type LayerCountTest struct {
	// contains filtered or unexported fields
}

LayerCountTest is a test helper for verifying layer counts. It requires exporting the image to inspect the manifest.

func NewLayerCountTest

func NewLayerCountTest(env *TestEnv, outputDir string) *LayerCountTest

NewLayerCountTest creates a new LayerCountTest helper. outputDir is the directory where the OCI layout will be exported for inspection.

func (*LayerCountTest) AssertLayerCount

func (l *LayerCountTest) AssertLayerCount(
	t *testing.T,
	originalLayers int,
	patchedLayers int,
	expectedAdded int,
)

AssertLayerCount asserts that the patched image has the expected number of layers. expectedOriginal is the number of layers in the original image. expectedAdded is the number of layers that should be added by patching (typically 1).

func (*LayerCountTest) ExportAndCountLayers

func (l *LayerCountTest) ExportAndCountLayers(
	ctx context.Context,
	t *testing.T,
	state *llb.State,
	platform *specs.Platform,
) (*ImageLayerInfo, error)

ExportAndCountLayers exports an image state to OCI layout and counts the layers. This is necessary because layer information cannot be inspected without export.

type PackageUpdate

type PackageUpdate struct {
	Name             string
	InstalledVersion string
	FixedVersion     string
	VulnerabilityID  string
}

PackageUpdate represents a package that needs to be updated.

type PatchTestConfig

type PatchTestConfig struct {
	// ImageName is the name of the image to patch (e.g., "alpine:3.18")
	ImageName string

	// Platform specifies the target platform for patching.
	// If nil, the default platform is used.
	Platform *specs.Platform

	// Updates is the vulnerability update manifest.
	// This specifies which packages to update and to what versions.
	Updates *unversioned.UpdateManifest

	// WorkingFolder is the temporary working directory.
	// If empty, a system temp directory is used.
	WorkingFolder string

	// IgnoreError continues patching even if some packages fail.
	IgnoreError bool

	// ExitOnEOL exits if the OS is end-of-life.
	ExitOnEOL bool
}

PatchTestConfig holds configuration for patch tests.

type PatchTestResult

type PatchTestResult struct {
	// Result is the underlying patch.Result from ExecutePatchCore.
	Result *patch.Result

	// Inspector provides filesystem inspection capabilities for the patched image.
	Inspector *RefInspector

	// PackageType is the detected package manager type (e.g., "deb", "apk", "rpm").
	PackageType string

	// ErroredPackages is the list of packages that failed to patch.
	ErroredPackages []string
}

PatchTestResult holds the result of a patch test.

type RefInspector

type RefInspector struct {
	// contains filtered or unexported fields
}

RefInspector provides methods to inspect a BuildKit Reference's filesystem. This allows tests to read files, check file existence, and list directories without exporting the image.

func NewRefInspector

func NewRefInspector(ctx context.Context, result *gwclient.Result) (*RefInspector, error)

NewRefInspector creates an inspector from a gateway Result. The result must contain exactly one reference (use SingleRef internally).

func NewRefInspectorFromRef

func NewRefInspectorFromRef(ctx context.Context, ref gwclient.Reference) (*RefInspector, error)

NewRefInspectorFromRef creates an inspector from a specific Reference. This is useful when working with multi-platform results where you need to inspect a specific platform's reference.

func (*RefInspector) AssertApkPackageVersion

func (r *RefInspector) AssertApkPackageVersion(t *testing.T, pkgName, expectedVersion string)

AssertApkPackageVersion asserts that an Alpine package is installed at the specified version. It reads /lib/apk/db/installed to check the package version.

func (*RefInspector) AssertDebPackageVersion

func (r *RefInspector) AssertDebPackageVersion(t *testing.T, pkgName, expectedVersion string)

AssertDebPackageVersion asserts that a Debian package is installed at the specified version. It reads /var/lib/dpkg/status to check the package version.

func (*RefInspector) AssertDirContainsFile

func (r *RefInspector) AssertDirContainsFile(t *testing.T, dirPath, fileName string)

AssertDirContainsFile asserts that a directory contains a file with the given name.

func (*RefInspector) AssertDirExists

func (r *RefInspector) AssertDirExists(t *testing.T, path string)

AssertDirExists asserts that a directory exists at the given path.

func (*RefInspector) AssertFileContains

func (r *RefInspector) AssertFileContains(t *testing.T, path, substr string)

AssertFileContains asserts that a file contains the given substring.

func (*RefInspector) AssertFileEquals

func (r *RefInspector) AssertFileEquals(t *testing.T, path string, expected []byte)

AssertFileEquals asserts that a file's content exactly matches the expected bytes.

func (*RefInspector) AssertFileExists

func (r *RefInspector) AssertFileExists(t *testing.T, path string)

AssertFileExists asserts that a file exists at the given path.

func (*RefInspector) AssertFileMatches

func (r *RefInspector) AssertFileMatches(t *testing.T, path, pattern string)

AssertFileMatches asserts that a file's content matches the given regex pattern.

func (*RefInspector) AssertFileNotContains

func (r *RefInspector) AssertFileNotContains(t *testing.T, path, substr string)

AssertFileNotContains asserts that a file does not contain the given substring.

func (*RefInspector) AssertFileNotExists

func (r *RefInspector) AssertFileNotExists(t *testing.T, path string)

AssertFileNotExists asserts that a file does not exist at the given path.

func (*RefInspector) AssertPackageVersion

func (r *RefInspector) AssertPackageVersion(t *testing.T, pkgType, pkgName, expectedVersion string)

AssertPackageVersion is a generic helper that detects the package manager type and calls the appropriate assertion function.

func (*RefInspector) AssertRpmPackageVersion

func (r *RefInspector) AssertRpmPackageVersion(t *testing.T, pkgName, expectedVersion string)

AssertRpmPackageVersion asserts that an RPM package is installed at the specified version. It looks for the package in the RPM database files.

func (*RefInspector) AssertSymlinkTarget

func (r *RefInspector) AssertSymlinkTarget(t *testing.T, path, expectedTarget string)

AssertSymlinkTarget asserts that a symlink points to the expected target.

func (*RefInspector) DirExists

func (r *RefInspector) DirExists(path string) bool

DirExists checks if a directory exists at the given path. Returns true if the path exists and is a directory.

func (*RefInspector) FileExists

func (r *RefInspector) FileExists(path string) bool

FileExists checks if a file exists at the given path. Returns true if the file exists, false otherwise.

func (r *RefInspector) IsSymlink(path string) bool

IsSymlink checks if the path is a symbolic link.

func (*RefInspector) ReadDir

func (r *RefInspector) ReadDir(path string) ([]*fstypes.Stat, error)

ReadDir lists the contents of a directory. Returns a slice of file stats for all entries in the directory.

func (*RefInspector) ReadFile

func (r *RefInspector) ReadFile(path string) ([]byte, error)

ReadFile reads the contents of a file at the given path. Returns the file contents as bytes, or an error if the file doesn't exist or cannot be read.

func (*RefInspector) ReadFileRange

func (r *RefInspector) ReadFileRange(path string, offset, length int) ([]byte, error)

ReadFileRange reads a range of bytes from a file. Offset specifies the starting position, length specifies how many bytes to read.

func (r *RefInspector) ReadSymlink(path string) (string, error)

ReadSymlink returns the target of a symbolic link.

func (*RefInspector) Reference

func (r *RefInspector) Reference() gwclient.Reference

Reference returns the underlying gateway Reference. This can be useful for advanced operations not covered by this inspector.

func (*RefInspector) StatFile

func (r *RefInspector) StatFile(path string) (*fstypes.Stat, error)

StatFile returns file metadata for the given path. This is useful for checking file permissions, size, modification time, etc.

type TestEnv

type TestEnv struct {
	// contains filtered or unexported fields
}

TestEnv manages BuildKit connections for integration testing. It provides a way to run tests with access to a gateway client, enabling direct inspection of container state.

func New

func New(addr string) *TestEnv

New creates a new TestEnv with the given BuildKit address. If addr is empty, it will auto-detect the BuildKit instance (trying docker://, buildx, then default unix socket).

func (*TestEnv) Addr

func (e *TestEnv) Addr() string

Addr returns the BuildKit address used by this test environment.

func (*TestEnv) Client

func (e *TestEnv) Client(ctx context.Context) (*client.Client, error)

Client returns the underlying BuildKit client, creating it if necessary. This can be useful for tests that need direct access to the client.

func (*TestEnv) Close

func (e *TestEnv) Close() error

Close closes the BuildKit client connection. It should be called when the test environment is no longer needed.

func (*TestEnv) RunPatchTest

func (e *TestEnv) RunPatchTest(ctx context.Context, t *testing.T, cfg PatchTestConfig) (*PatchTestResult, error)

RunPatchTest executes a patch operation and returns results. This is a convenience wrapper around RunTest and ExecutePatchCore.

WARNING: The Inspector field in the returned PatchTestResult will have an invalid reference after this function returns, because the BuildKit job is cleaned up when the Build callback completes. If you need to inspect the patched filesystem, use RunPatchTestWithInspection instead, which performs inspection inside the Build callback where the reference is valid.

This function is still useful for tests that only need to check metadata like PackageType and ErroredPackages, but NOT for filesystem inspection.

Example usage (metadata only - DO NOT use Inspector):

result, err := env.RunPatchTest(ctx, t, testenv.PatchTestConfig{
    ImageName: "alpine:3.18",
    Platform:  &specs.Platform{OS: "linux", Architecture: "amd64"},
    Updates:   updates,
})
require.NoError(t, err)
t.Logf("Package type: %s", result.PackageType)
// NOTE: result.Inspector methods will fail with "no such job" error!

func (*TestEnv) RunPatchTestWithInspection

func (e *TestEnv) RunPatchTestWithInspection(
	ctx context.Context,
	t *testing.T,
	cfg PatchTestConfig,
	inspect func(ctx context.Context, t *testing.T, c gwclient.Client, result *patch.Result),
) error

RunPatchTestWithInspection is similar to RunPatchTest but allows custom inspection logic within the gateway client context. This is useful when you need to perform multiple operations with the same gateway client, such as comparing pre/post patch states.

Example usage:

err := env.RunPatchTestWithInspection(ctx, t, cfg, func(ctx context.Context, t *testing.T, c gwclient.Client, result *patch.Result) {
    // Inspect the patched result
    inspector, _ := testenv.NewRefInspector(ctx, result.Result)
    inspector.AssertFileExists(t, "/etc/os-release")

    // You can also perform additional operations with the gateway client here
})

func (*TestEnv) RunTest

func (e *TestEnv) RunTest(ctx context.Context, t *testing.T, f TestFunc, opts ...TestRunnerOpt)

RunTest executes a test function with a gateway client. The gateway client is obtained via client.Build() callback, which provides the context needed for LLB operations and result inspection.

Example usage:

env := testenv.New(os.Getenv("COPA_BUILDKIT_ADDR"))
defer env.Close()

env.RunTest(ctx, t, func(ctx context.Context, t *testing.T, c gwclient.Client) {
    // Use gateway client to resolve images, build, inspect results
    _, _, cfg, err := c.ResolveImageConfig(ctx, "alpine:latest", sourceresolver.Opt{})
    require.NoError(t, err)
    // ... assertions
})

type TestFunc

type TestFunc func(ctx context.Context, t *testing.T, c gwclient.Client)

TestFunc is the function signature for tests that receive a gateway client. Tests can use the gateway client to resolve images, build LLB definitions, and inspect results without exporting.

type TestRunnerConfig

type TestRunnerConfig struct {
	// SolveOpts are the options passed to the BuildKit solve operation.
	SolveOpts client.SolveOpt

	// SkipExport skips image export (useful for inspection-only tests).
	// When true, SolveOpts.Exports will be set to nil.
	SkipExport bool
}

TestRunnerConfig holds configuration for test execution.

type TestRunnerOpt

type TestRunnerOpt func(*TestRunnerConfig)

TestRunnerOpt is a functional option for configuring test runs.

func WithExportToImage

func WithExportToImage(imageName string, push bool) TestRunnerOpt

WithExportToImage configures the test to export to a container image.

func WithExportToOCI

func WithExportToOCI(outputDir string) TestRunnerOpt

WithExportToOCI configures the test to export to an OCI layout directory. This is useful for tests that need to inspect the final image layers.

func WithExportToTar

func WithExportToTar(outputPath string) TestRunnerOpt

WithExportToTar configures the test to export to a tarball.

func WithSkipExport

func WithSkipExport() TestRunnerOpt

WithSkipExport skips image export for the test. This is useful for tests that only need to inspect the built state without exporting to a registry or tarball.

func WithSolveOpts

func WithSolveOpts(opts *client.SolveOpt) TestRunnerOpt

WithSolveOpts sets custom solve options for the test. This allows tests to configure exports, cache options, etc.

Jump to

Keyboard shortcuts

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