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 ¶
- func CompareLayerCounts(ctx context.Context, c gwclient.Client, originalImage string, ...) (int, int, error)
- func CreateUpdateManifest(osType, osVersion, arch string, packages []PackageUpdate) *unversioned.UpdateManifest
- func NormalizeImageRef(imageRef string) string
- type ImageLayerInfo
- type LayerCountTest
- type PackageUpdate
- type PatchTestConfig
- type PatchTestResult
- type RefInspector
- func (r *RefInspector) AssertApkPackageVersion(t *testing.T, pkgName, expectedVersion string)
- func (r *RefInspector) AssertDebPackageVersion(t *testing.T, pkgName, expectedVersion string)
- func (r *RefInspector) AssertDirContainsFile(t *testing.T, dirPath, fileName string)
- func (r *RefInspector) AssertDirExists(t *testing.T, path string)
- func (r *RefInspector) AssertFileContains(t *testing.T, path, substr string)
- func (r *RefInspector) AssertFileEquals(t *testing.T, path string, expected []byte)
- func (r *RefInspector) AssertFileExists(t *testing.T, path string)
- func (r *RefInspector) AssertFileMatches(t *testing.T, path, pattern string)
- func (r *RefInspector) AssertFileNotContains(t *testing.T, path, substr string)
- func (r *RefInspector) AssertFileNotExists(t *testing.T, path string)
- func (r *RefInspector) AssertPackageVersion(t *testing.T, pkgType, pkgName, expectedVersion string)
- func (r *RefInspector) AssertRpmPackageVersion(t *testing.T, pkgName, expectedVersion string)
- func (r *RefInspector) AssertSymlinkTarget(t *testing.T, path, expectedTarget string)
- func (r *RefInspector) DirExists(path string) bool
- func (r *RefInspector) FileExists(path string) bool
- func (r *RefInspector) IsSymlink(path string) bool
- func (r *RefInspector) ReadDir(path string) ([]*fstypes.Stat, error)
- func (r *RefInspector) ReadFile(path string) ([]byte, error)
- func (r *RefInspector) ReadFileRange(path string, offset, length int) ([]byte, error)
- func (r *RefInspector) ReadSymlink(path string) (string, error)
- func (r *RefInspector) Reference() gwclient.Reference
- func (r *RefInspector) StatFile(path string) (*fstypes.Stat, error)
- type TestEnv
- func (e *TestEnv) Addr() string
- func (e *TestEnv) Client(ctx context.Context) (*client.Client, error)
- func (e *TestEnv) Close() error
- func (e *TestEnv) RunPatchTest(ctx context.Context, t *testing.T, cfg PatchTestConfig) (*PatchTestResult, error)
- func (e *TestEnv) RunPatchTestWithInspection(ctx context.Context, t *testing.T, cfg PatchTestConfig, ...) error
- func (e *TestEnv) RunTest(ctx context.Context, t *testing.T, f TestFunc, opts ...TestRunnerOpt)
- type TestFunc
- type TestRunnerConfig
- type TestRunnerOpt
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 ¶
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 ¶
NewRefInspector creates an inspector from a gateway Result. The result must contain exactly one reference (use SingleRef internally).
func NewRefInspectorFromRef ¶
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 (*RefInspector) IsSymlink ¶
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 (*RefInspector) ReadSymlink ¶
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.
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 ¶
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) Client ¶
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 ¶
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 ¶
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 ¶
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.