Compare commits

...

27 Commits

Author SHA1 Message Date
Kohei Tokunaga 554629b4a1
Merge pull request #2105 from containerd/dependabot/go_modules/containerd-2f3dabfc34
build(deps): bump the containerd group across 2 directories with 1 update
2025-08-11 21:37:26 +09:00
Kohei Tokunaga a986f8c194
Merge pull request #2101 from containerd/dependabot/github_actions/actions/download-artifact-5
build(deps): bump actions/download-artifact from 4 to 5
2025-08-10 16:50:38 +09:00
Kohei Tokunaga 38cf9aa57c
Merge pull request #2104 from containerd/dependabot/go_modules/cmd/gomod-cfb98d732f
build(deps): bump the gomod group across 2 directories with 1 update
2025-08-09 12:56:57 +09:00
dependabot[bot] e07c7abb8c
build(deps): bump the containerd group across 2 directories with 1 update
Bumps the containerd group with 1 update in the / directory: [github.com/containerd/go-cni](https://github.com/containerd/go-cni).
Bumps the containerd group with 1 update in the /cmd directory: [github.com/containerd/go-cni](https://github.com/containerd/go-cni).


Updates `github.com/containerd/go-cni` from 1.1.12 to 1.1.13
- [Release notes](https://github.com/containerd/go-cni/releases)
- [Commits](https://github.com/containerd/go-cni/compare/v1.1.12...v1.1.13)

Updates `github.com/containerd/go-cni` from 1.1.12 to 1.1.13
- [Release notes](https://github.com/containerd/go-cni/releases)
- [Commits](https://github.com/containerd/go-cni/compare/v1.1.12...v1.1.13)

---
updated-dependencies:
- dependency-name: github.com/containerd/go-cni
  dependency-version: 1.1.13
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: containerd
- dependency-name: github.com/containerd/go-cni
  dependency-version: 1.1.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: containerd
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-08 21:07:10 +00:00
dependabot[bot] 7b508b1afe
build(deps): bump the gomod group across 2 directories with 1 update
Bumps the gomod group with 1 update in the /cmd directory: [github.com/multiformats/go-multiaddr](https://github.com/multiformats/go-multiaddr).
Bumps the gomod group with 1 update in the /ipfs directory: [github.com/multiformats/go-multiaddr](https://github.com/multiformats/go-multiaddr).


Updates `github.com/multiformats/go-multiaddr` from 0.16.0 to 0.16.1
- [Release notes](https://github.com/multiformats/go-multiaddr/releases)
- [Commits](https://github.com/multiformats/go-multiaddr/compare/v0.16.0...v0.16.1)

Updates `github.com/multiformats/go-multiaddr` from 0.16.0 to 0.16.1
- [Release notes](https://github.com/multiformats/go-multiaddr/releases)
- [Commits](https://github.com/multiformats/go-multiaddr/compare/v0.16.0...v0.16.1)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-multiaddr
  dependency-version: 0.16.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: gomod
- dependency-name: github.com/multiformats/go-multiaddr
  dependency-version: 0.16.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: gomod
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-07 20:39:58 +00:00
Kohei Tokunaga 08272a30af
Merge pull request #2102 from wswsmao/main
ctr-remote: support 'all' for --gpus flag
2025-08-07 23:14:01 +09:00
abushwang d09a4ffa5b ctr-remote: support 'all' for --gpus flag
Signed-off-by: abushwang <abushwang@tencent.com>
2025-08-07 20:13:47 +08:00
dependabot[bot] 1227c8e42b
build(deps): bump actions/download-artifact from 4 to 5
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-06 20:29:12 +00:00
Kohei Tokunaga 21fb6a16e4
Merge pull request #2100 from containerd/dependabot/go_modules/gomod-5d31edd0e2
build(deps): bump the gomod group across 2 directories with 1 update
2025-08-01 20:28:39 +09:00
dependabot[bot] f400f935f7
build(deps): bump the gomod group across 2 directories with 1 update
Bumps the gomod group with 1 update in the / directory: [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang).
Bumps the gomod group with 1 update in the /cmd directory: [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang).


Updates `github.com/prometheus/client_golang` from 1.22.0 to 1.23.0
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.23.0/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.22.0...v1.23.0)

Updates `github.com/prometheus/client_golang` from 1.22.0 to 1.23.0
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.23.0/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.22.0...v1.23.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-version: 1.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gomod
- dependency-name: github.com/prometheus/client_golang
  dependency-version: 1.23.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: gomod
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-31 20:34:42 +00:00
Kohei Tokunaga d0487729a1
Merge pull request #2098 from rosstimothy/tross/remove_testing
Remove `testing` dependency from code outside tests
2025-08-01 00:34:36 +09:00
Tim Ross 2aef7a6e63
Replace testing.T references with local TestingT interface
Removes the dependency on the `testing` package in the remaining
testutil packages by defining a local TestingT interface which
contains the subset of `testing.T` required for the test helpers.

Signed-off-by: Tim Ross <tim.ross@goteleport.com>
2025-07-31 10:13:18 -04:00
Tim Ross 18e393af96
Remove testing from util/testutil package
RandomBytes now returns an error instead of taking a testing.T
and asserting the error is non nil to eliminate dependence on the
testing package outside of test files.

Signed-off-by: Tim Ross <tim.ross@goteleport.com>
2025-07-31 10:13:17 -04:00
Kohei Tokunaga 927075ba7b
Merge pull request #2099 from containerd/dependabot/go_modules/containerd-24169ab9f1
build(deps): bump the containerd group across 3 directories with 1 update
2025-07-31 09:56:52 +09:00
dependabot[bot] e53cec5266
build(deps): bump the containerd group across 3 directories with 1 update
Bumps the containerd group with 1 update in the / directory: [github.com/containerd/containerd/v2](https://github.com/containerd/containerd).
Bumps the containerd group with 1 update in the /cmd directory: [github.com/containerd/containerd/v2](https://github.com/containerd/containerd).
Bumps the containerd group with 1 update in the /ipfs directory: [github.com/containerd/containerd/v2](https://github.com/containerd/containerd).


Updates `github.com/containerd/containerd/v2` from 2.1.3 to 2.1.4
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.1.3...v2.1.4)

Updates `github.com/containerd/containerd/v2` from 2.1.3 to 2.1.4
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.1.3...v2.1.4)

Updates `github.com/containerd/containerd/v2` from 2.1.3 to 2.1.4
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.1.3...v2.1.4)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.1.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: containerd
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.1.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: containerd
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.1.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: containerd
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-30 20:58:05 +00:00
Kohei Tokunaga 555e167719
Merge pull request #2096 from containerd/dependabot/go_modules/gomod-f8c008d7c4
build(deps): bump the gomod group across 2 directories with 1 update
2025-07-30 10:00:29 +09:00
dependabot[bot] f72d3aecc8
build(deps): bump the gomod group across 2 directories with 1 update
Bumps the gomod group with 1 update in the / directory: [github.com/docker/cli](https://github.com/docker/cli).
Bumps the gomod group with 1 update in the /cmd directory: [github.com/docker/cli](https://github.com/docker/cli).


Updates `github.com/docker/cli` from 28.3.2+incompatible to 28.3.3+incompatible
- [Commits](https://github.com/docker/cli/compare/v28.3.2...v28.3.3)

Updates `github.com/docker/cli` from 28.3.2+incompatible to 28.3.3+incompatible
- [Commits](https://github.com/docker/cli/compare/v28.3.2...v28.3.3)

---
updated-dependencies:
- dependency-name: github.com/docker/cli
  dependency-version: 28.3.3+incompatible
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: gomod
- dependency-name: github.com/docker/cli
  dependency-version: 28.3.3+incompatible
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: gomod
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-29 20:59:10 +00:00
Kohei Tokunaga 19a5bd5157
Merge pull request #2095 from wswsmao/main
Add FadvDontNeed option to avoid double pagecache consumption
2025-07-29 18:59:43 +09:00
abushwang f62679453f Add FadvDontNeed option to avoid double pagecache consumption
Signed-off-by: abushwang <abushwang@tencent.com>
2025-07-29 16:24:11 +08:00
Kohei Tokunaga 17134ee517
Merge pull request #2092 from wswsmao/main
docs: Add Unexpected restart handling
2025-07-26 16:44:47 +09:00
abushwang 420a03fa46 docs: Add Unexpected restart handling
Signed-off-by: abushwang <abushwang@tencent.com>
2025-07-24 19:29:13 +08:00
Kohei Tokunaga 3aa69eaac5
Merge pull request #2091 from wswsmao/main
Fix file exists error when restoring remote snapshot after unexpected…
2025-07-24 15:41:38 +09:00
abushwang b3743e7916 Fix file exists error when restoring remote snapshot after unexpected restart
Signed-off-by: abushwang <abushwang@tencent.com>
2025-07-23 20:57:24 +08:00
Kohei Tokunaga 4677721cfc
Merge pull request #2089 from containerd/dependabot/docker/kindest/node-v1.33.2
build(deps): bump kindest/node from v1.33.1 to v1.33.2
2025-07-23 10:32:09 +09:00
Kohei Tokunaga 9e6b9b72af
Merge pull request #2090 from containerd/dependabot/go_modules/google-golang-d7ee4b52d7
build(deps): bump the google-golang group across 3 directories with 1 update
2025-07-23 10:31:44 +09:00
dependabot[bot] cfa57c95a9
build(deps): bump the google-golang group across 3 directories with 1 update
Bumps the google-golang group with 1 update in the / directory: [google.golang.org/grpc](https://github.com/grpc/grpc-go).
Bumps the google-golang group with 1 update in the /cmd directory: [google.golang.org/grpc](https://github.com/grpc/grpc-go).
Bumps the google-golang group with 1 update in the /ipfs directory: [google.golang.org/grpc](https://github.com/grpc/grpc-go).


Updates `google.golang.org/grpc` from 1.73.0 to 1.74.2
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.73.0...v1.74.2)

Updates `google.golang.org/grpc` from 1.73.0 to 1.74.2
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.73.0...v1.74.2)

Updates `google.golang.org/grpc` from 1.73.0 to 1.74.2
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.73.0...v1.74.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.74.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: google-golang
- dependency-name: google.golang.org/grpc
  dependency-version: 1.74.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: google-golang
- dependency-name: google.golang.org/grpc
  dependency-version: 1.74.2
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: google-golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-22 20:55:46 +00:00
dependabot[bot] 24e8a7858a
build(deps): bump kindest/node from v1.33.1 to v1.33.2
Bumps kindest/node from v1.33.1 to v1.33.2.

---
updated-dependencies:
- dependency-name: kindest/node
  dependency-version: v1.33.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-22 20:03:31 +00:00
27 changed files with 652 additions and 324 deletions

View File

@ -50,7 +50,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Download Builds
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
path: ${{ env.OUTPUT_DIR }}
- name: Create Release

View File

@ -246,7 +246,7 @@ RUN apt-get update && apt-get install -y iptables && \
curl -Ls https://github.com/containernetworking/plugins/releases/download/${CNI_PLUGINS_VERSION}/cni-plugins-linux-${TARGETARCH:-amd64}-${CNI_PLUGINS_VERSION}.tgz | tar xzv -C /opt/cni/bin
# Image which can be used as a node image for KinD (containerd with builtin snapshotter)
FROM kindest/node:v1.33.1 AS kind-builtin-snapshotter
FROM kindest/node:v1.33.2 AS kind-builtin-snapshotter
COPY --from=containerd-snapshotter-dev /out/bin/containerd /out/bin/containerd-shim-runc-v2 /usr/local/bin/
COPY --from=snapshotter-dev /out/ctr-remote /usr/local/bin/
COPY ./script/config/ /
@ -287,7 +287,7 @@ COPY ./script/config-cri-o/ /
ENTRYPOINT [ "/usr/local/bin/entrypoint" ]
# Image which can be used as a node image for KinD
FROM kindest/node:v1.33.1
FROM kindest/node:v1.33.2
COPY --from=containerd-dev /out/bin/containerd /out/bin/containerd-shim-runc-v2 /usr/local/bin/
COPY --from=snapshotter-dev /out/* /usr/local/bin/
COPY ./script/config/ /

32
cache/cache.go vendored
View File

@ -27,6 +27,7 @@ import (
"github.com/containerd/stargz-snapshotter/util/cacheutil"
"github.com/containerd/stargz-snapshotter/util/namedmutex"
"golang.org/x/sys/unix"
)
const (
@ -61,6 +62,9 @@ type DirectoryCacheConfig struct {
// Direct forcefully enables direct mode for all operation in cache.
// Thus operation won't use on-memory caches.
Direct bool
// FadvDontNeed forcefully clean fscache pagecache for saving memory.
FadvDontNeed bool
}
// TODO: contents validation.
@ -173,6 +177,7 @@ func NewDirectoryCache(directory string, config DirectoryCacheConfig) (BlobCache
wipDirectory: wipdir,
bufPool: bufPool,
direct: config.Direct,
fadvDontNeed: config.FadvDontNeed,
}
dc.syncAdd = config.SyncAdd
return dc, nil
@ -190,6 +195,7 @@ type directoryCache struct {
syncAdd bool
direct bool
fadvDontNeed bool
closed bool
closedMu sync.Mutex
@ -244,6 +250,12 @@ func (dc *directoryCache) Get(key string, opts ...Option) (Reader, error) {
return &reader{
ReaderAt: file,
closeFunc: func() error {
if dc.fadvDontNeed {
if err := dropFilePageCache(file); err != nil {
fmt.Printf("Warning: failed to drop page cache: %v\n", err)
}
}
// In passthough model, close will be toke over by go-fuse
// If "passThrough" option is specified, "direct" option also will
// be specified, so adding this branch here is enough
@ -301,6 +313,13 @@ func (dc *directoryCache) Add(key string, opts ...Option) (Writer, error) {
errs = append(errs, fmt.Errorf("failed to create cache directory %q: %w", c, err))
return errors.Join(errs...)
}
if dc.fadvDontNeed {
if err := dropFilePageCache(wip); err != nil {
fmt.Printf("Warning: failed to drop page cache: %v\n", err)
}
}
return os.Rename(wip.Name(), c)
},
abortFunc: func() error {
@ -463,3 +482,16 @@ func (w *writeCloser) Close() error { return w.closeFunc() }
func nopWriteCloser(w io.Writer) io.WriteCloser {
return &writeCloser{w, func() error { return nil }}
}
func dropFilePageCache(file *os.File) error {
if file == nil {
return nil
}
fd := file.Fd()
err := unix.Fadvise(int(fd), 0, 0, unix.FADV_DONTNEED)
if err != nil {
return fmt.Errorf("posix_fadvise failed, ret=%d", err)
}
return nil
}

View File

@ -29,15 +29,60 @@ import (
)
func TestReader(t *testing.T) {
testutil.TestReader(t, newTestableReader)
testRunner := &testutil.TestRunner{
TestingT: t,
Runner: func(testingT testutil.TestingT, name string, run func(t testutil.TestingT)) {
tt, ok := testingT.(*testing.T)
if !ok {
testingT.Fatal("TestingT is not a *testing.T")
return
}
tt.Run(name, func(t *testing.T) {
run(t)
})
},
}
testutil.TestReader(testRunner, newTestableReader)
}
func TestFSReader(t *testing.T) {
fsreader.TestSuiteReader(t, newStore)
testRunner := &fsreader.TestRunner{
TestingT: t,
Runner: func(testingT fsreader.TestingT, name string, run func(t fsreader.TestingT)) {
tt, ok := testingT.(*testing.T)
if !ok {
testingT.Fatal("TestingT is not a *testing.T")
return
}
tt.Run(name, func(t *testing.T) {
run(t)
})
},
}
fsreader.TestSuiteReader(testRunner, newStore)
}
func TestFSLayer(t *testing.T) {
layer.TestSuiteLayer(t, newStore)
testRunner := &layer.TestRunner{
TestingT: t,
Runner: func(testingT layer.TestingT, name string, run func(t layer.TestingT)) {
tt, ok := testingT.(*testing.T)
if !ok {
testingT.Fatal("TestingT is not a *testing.T")
return
}
tt.Run(name, func(t *testing.T) {
run(t)
})
},
}
layer.TestSuiteLayer(testRunner, newStore)
}
func newTestableReader(sr *io.SectionReader, opts ...metadata.Option) (testutil.TestableReader, error) {

View File

@ -26,6 +26,7 @@ import (
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
containerd "github.com/containerd/containerd/v2/client"
@ -45,6 +46,24 @@ import (
const netnsMountDir = "/var/run/netns"
func parseGPUs(gpuStr string) ([]int, bool) {
if gpuStr == "" {
return nil, false
}
if gpuStr == "all" {
return nil, true
}
parts := strings.Split(gpuStr, ",")
var devices []int
for _, part := range parts {
part = strings.TrimSpace(part)
if device, err := strconv.Atoi(part); err == nil {
devices = append(devices, device)
}
}
return devices, false
}
var samplerFlags = []cli.Flag{
&cli.BoolFlag{
Name: "terminal,t",
@ -116,9 +135,9 @@ var samplerFlags = []cli.Flag{
Name: "cni-plugin-dir",
Usage: "path to the CNI plugins binary directory",
},
&cli.IntSliceFlag{
&cli.StringFlag{
Name: "gpus",
Usage: "add gpus to the container",
Usage: "add gpus to the container (comma-separated list of indices or 'all')",
},
&cli.BoolFlag{
Name: "net-host",
@ -213,7 +232,12 @@ func getSpecOpts(clicontext *cli.Context) func(image containerd.Image, rootfs st
if runtime.GOOS == "windows" {
log.L.Warn("option --gpus is not supported on Windows")
} else {
opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(clicontext.IntSlice("gpus")...), nvidia.WithAllCapabilities))
devices, useAll := parseGPUs(clicontext.String("gpus"))
if useAll {
opts = append(opts, nvidia.WithGPUs(nvidia.WithAllCapabilities))
} else if len(devices) > 0 {
opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(devices...), nvidia.WithAllCapabilities))
}
}
}

View File

@ -6,8 +6,8 @@ toolchain go1.24.2
require (
github.com/containerd/containerd/api v1.9.0
github.com/containerd/containerd/v2 v2.1.3
github.com/containerd/go-cni v1.1.12
github.com/containerd/containerd/v2 v2.1.4
github.com/containerd/go-cni v1.1.13
github.com/containerd/log v0.1.0
github.com/containerd/platforms v1.0.0-rc.1
github.com/containerd/stargz-snapshotter v0.15.2-0.20240622031358-6405f362966d
@ -26,7 +26,7 @@ require (
go.etcd.io/bbolt v1.4.2
golang.org/x/sync v0.16.0
golang.org/x/sys v0.34.0
google.golang.org/grpc v1.73.0
google.golang.org/grpc v1.74.2
)
require (
@ -50,14 +50,14 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v28.3.2+incompatible // indirect
github.com/docker/cli v28.3.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
@ -93,7 +93,7 @@ require (
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr v0.16.0 // indirect
github.com/multiformats/go-multiaddr v0.16.1 // indirect
github.com/multiformats/go-multibase v0.2.0 // indirect
github.com/multiformats/go-multihash v0.2.3 // indirect
github.com/multiformats/go-varint v0.0.7 // indirect
@ -103,10 +103,10 @@ require (
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_golang v1.23.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
@ -119,18 +119,18 @@ require (
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.28.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/time v0.9.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect

View File

@ -27,8 +27,8 @@ github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/q
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
github.com/containerd/containerd/v2 v2.1.3 h1:eMD2SLcIQPdMlnlNF6fatlrlRLAeDaiGPGwmRKLZKNs=
github.com/containerd/containerd/v2 v2.1.3/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ=
github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@ -37,8 +37,8 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/containerd/go-cni v1.1.12 h1:wm/5VD/i255hjM4uIZjBRiEQ7y98W9ACy/mHeLi4+94=
github.com/containerd/go-cni v1.1.12/go.mod h1:+jaqRBdtW5faJxj2Qwg1Of7GsV66xcvnCx4mSJtUlxU=
github.com/containerd/go-cni v1.1.13 h1:eFSGOKlhoYNxpJ51KRIMHZNlg5UgocXEIEBGkY7Hnis=
github.com/containerd/go-cni v1.1.13/go.mod h1:nTieub0XDRmvCZ9VI/SBG6PyqT95N4FIhxsauF1vSBI=
github.com/containerd/go-runc v1.1.0 h1:OX4f+/i2y5sUT7LhmcJH7GYrjjhHa1QI4e8yO0gGleA=
github.com/containerd/go-runc v1.1.0/go.mod h1:xJv2hFF7GvHtTJd9JqTS2UVxMkULUYw4JN5XAUZqH5U=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
@ -66,8 +66,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY=
github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.3.3+incompatible h1:fp9ZHAr1WWPGdIWBM1b3zLtgCF+83gRdVMTJsUeiyAo=
github.com/docker/cli v28.3.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
@ -92,8 +92,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
@ -248,8 +248,8 @@ github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYg
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc=
github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0=
github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw=
github.com/multiformats/go-multiaddr v0.16.1/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
@ -293,22 +293,22 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
@ -369,16 +369,16 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@ -389,8 +389,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
@ -413,11 +413,11 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -444,12 +444,12 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -471,15 +471,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=

View File

@ -74,10 +74,11 @@ You can enable host networking for the container using the `net-host` flag.
# ctr-remote i optimize -t -i --oci --entrypoint='[ "/bin/bash", "-c" ]' --net-host --args='[ "ip a && curl example.com" ]' ghcr.io/stargz-containers/centos:8-test registry2:5000/centos:8-test-esgz
```
You can optimize GPU-based images using the `gpu` flag. The flag expects a comma separated list of integers.
You can optimize GPU-based images using the `gpu` flag. The flag expects a comma separated list of integers or 'all'.
```console
# ctr-remote i optimize --oci --gpus "0" <src> <target>
# ctr-remote i optimize --oci --gpus "all" <src> <target>
```
`--oci` option is highly recommended to add when you create eStargz image.

View File

@ -144,6 +144,14 @@ When stopping FUSE manager for upgrading the binary or restarting the node, you
4. Restart the containerd-stargz-grpc process. This restores all snapshot mounts by lazy pulling them. `allow_invalid_mounts_on_restart` (described in the above) can still be used for controlling the behaviour of the error cases.
5. Restart the containers.
### Unexpected restart handling
When Stargz Snapshotter is killed unexpectedly (e.g., by OOM killer or system crash), the process doesn't get a chance to perform graceful cleanup. In such cases, the snapshotter can successfully restart and restore remote snapshots, but this may lead to fscache duplicating cached data.
**Recommended handling:**
Since this scenario is caused by abnormal exit, users are expected to manually clean up the cache directory after an unexpected restart to avoid cache duplication issues. The cache cleanup should be performed before restarting the snapshotter service.
## Registry-related configuration
You can configure stargz snapshotter for accessing registries with custom configurations.

View File

@ -27,7 +27,22 @@ import (
// TestGzipEStargz tests gzip-based external TOC eStargz
func TestGzipEStargz(t *testing.T) {
estargz.CompressionTestSuite(t,
testRunner := &estargz.TestRunner{
TestingT: t,
Runner: func(testingT estargz.TestingT, name string, run func(t estargz.TestingT)) {
tt, ok := testingT.(*testing.T)
if !ok {
testingT.Fatal("TestingT is not a *testing.T")
return
}
tt.Run(name, func(t *testing.T) {
run(t)
})
},
}
estargz.CompressionTestSuite(testRunner,
gzipControllerWithLevel(gzip.NoCompression),
gzipControllerWithLevel(gzip.BestSpeed),
gzipControllerWithLevel(gzip.BestCompression),
@ -60,11 +75,11 @@ func (gc *gzipController) String() string {
}
// TestStream tests the passed estargz blob contains the specified list of streams.
func (gc *gzipController) TestStreams(t *testing.T, b []byte, streams []int64) {
func (gc *gzipController) TestStreams(t estargz.TestingT, b []byte, streams []int64) {
estargz.CheckGzipHasStreams(t, b, streams)
}
func (gc *gzipController) DiffIDOf(t *testing.T, b []byte) string {
func (gc *gzipController) DiffIDOf(t estargz.TestingT, b []byte) string {
return estargz.GzipDiffIDOf(t, b)
}

View File

@ -31,7 +31,22 @@ import (
// TestGzipEStargz tests gzip-based eStargz
func TestGzipEStargz(t *testing.T) {
CompressionTestSuite(t,
testRunner := &TestRunner{
TestingT: t,
Runner: func(testingT TestingT, name string, run func(t TestingT)) {
tt, ok := testingT.(*testing.T)
if !ok {
testingT.Fatal("TestingT is not a *testing.T")
return
}
tt.Run(name, func(t *testing.T) {
run(t)
})
},
}
CompressionTestSuite(testRunner,
gzipControllerWithLevel(gzip.NoCompression),
gzipControllerWithLevel(gzip.BestSpeed),
gzipControllerWithLevel(gzip.BestCompression),
@ -56,11 +71,11 @@ func (gc *gzipController) String() string {
}
// TestStream tests the passed estargz blob contains the specified list of streams.
func (gc *gzipController) TestStreams(t *testing.T, b []byte, streams []int64) {
func (gc *gzipController) TestStreams(t TestingT, b []byte, streams []int64) {
CheckGzipHasStreams(t, b, streams)
}
func (gc *gzipController) DiffIDOf(t *testing.T, b []byte) string {
func (gc *gzipController) DiffIDOf(t TestingT, b []byte) string {
return GzipDiffIDOf(t, b)
}

View File

@ -38,7 +38,6 @@ import (
"reflect"
"sort"
"strings"
"testing"
"time"
"github.com/containerd/stargz-snapshotter/estargz/errorutil"
@ -49,16 +48,48 @@ import (
// TestingController is Compression with some helper methods necessary for testing.
type TestingController interface {
Compression
TestStreams(t *testing.T, b []byte, streams []int64)
DiffIDOf(*testing.T, []byte) string
TestStreams(t TestingT, b []byte, streams []int64)
DiffIDOf(TestingT, []byte) string
String() string
}
// TestingT is the minimal set of testing.T required to run the
// tests defined in CompressionTestSuite. This interface exists to prevent
// leaking the testing package from being exposed outside tests.
type TestingT interface {
Errorf(format string, args ...any)
FailNow()
Failed() bool
Fatal(args ...any)
Fatalf(format string, args ...any)
Logf(format string, args ...any)
Parallel()
}
// Runner allows running subtests of TestingT. This exists instead of adding
// a Run method to TestingT interface because the Run implementation of
// testing.T would not satisfy the interface.
type Runner func(t TestingT, name string, fn func(t TestingT))
type TestRunner struct {
TestingT
Runner Runner
}
func (r *TestRunner) Run(name string, run func(*TestRunner)) {
r.Runner(r.TestingT, name, func(t TestingT) {
run(&TestRunner{TestingT: t, Runner: r.Runner})
})
}
// CompressionTestSuite tests this pkg with controllers can build valid eStargz blobs and parse them.
func CompressionTestSuite(t *testing.T, controllers ...TestingControllerFactory) {
t.Run("testBuild", func(t *testing.T) { t.Parallel(); testBuild(t, controllers...) })
t.Run("testDigestAndVerify", func(t *testing.T) { t.Parallel(); testDigestAndVerify(t, controllers...) })
t.Run("testWriteAndOpen", func(t *testing.T) { t.Parallel(); testWriteAndOpen(t, controllers...) })
func CompressionTestSuite(t *TestRunner, controllers ...TestingControllerFactory) {
t.Run("testBuild", func(t *TestRunner) { t.Parallel(); testBuild(t, controllers...) })
t.Run("testDigestAndVerify", func(t *TestRunner) {
t.Parallel()
testDigestAndVerify(t, controllers...)
})
t.Run("testWriteAndOpen", func(t *TestRunner) { t.Parallel(); testWriteAndOpen(t, controllers...) })
}
type TestingControllerFactory func() TestingController
@ -79,7 +110,7 @@ var allowedPrefix = [4]string{"", "./", "/", "../"}
// testBuild tests the resulting stargz blob built by this pkg has the same
// contents as the normal stargz blob.
func testBuild(t *testing.T, controllers ...TestingControllerFactory) {
func testBuild(t *TestRunner, controllers ...TestingControllerFactory) {
tests := []struct {
name string
chunkSize int
@ -165,7 +196,7 @@ func testBuild(t *testing.T, controllers ...TestingControllerFactory) {
prefix := prefix
for _, minChunkSize := range tt.minChunkSize {
minChunkSize := minChunkSize
t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,src=%d,format=%s,minChunkSize=%d", newCL(), prefix, srcCompression, srcTarFormat, minChunkSize), func(t *testing.T) {
t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,src=%d,format=%s,minChunkSize=%d", newCL(), prefix, srcCompression, srcTarFormat, minChunkSize), func(t *TestRunner) {
tarBlob := buildTar(t, tt.in, prefix, srcTarFormat)
// Test divideEntries()
entries, err := sortEntries(tarBlob, nil, nil) // identical order
@ -265,7 +296,7 @@ func testBuild(t *testing.T, controllers ...TestingControllerFactory) {
}
}
func isSameTarGz(t *testing.T, cla TestingController, a []byte, clb TestingController, b []byte) bool {
func isSameTarGz(t TestingT, cla TestingController, a []byte, clb TestingController, b []byte) bool {
aGz, err := cla.Reader(bytes.NewReader(a))
if err != nil {
t.Fatalf("failed to read A")
@ -325,7 +356,7 @@ func isSameTarGz(t *testing.T, cla TestingController, a []byte, clb TestingContr
return true
}
func isSameVersion(t *testing.T, cla TestingController, a []byte, clb TestingController, b []byte) bool {
func isSameVersion(t TestingT, cla TestingController, a []byte, clb TestingController, b []byte) bool {
aJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(a), 0, int64(len(a))), cla)
if err != nil {
t.Fatalf("failed to parse A: %v", err)
@ -339,7 +370,7 @@ func isSameVersion(t *testing.T, cla TestingController, a []byte, clb TestingCon
return aJTOC.Version == bJTOC.Version
}
func isSameEntries(t *testing.T, a, b *Reader) bool {
func isSameEntries(t TestingT, a, b *Reader) bool {
aroot, ok := a.Lookup("")
if !ok {
t.Fatalf("failed to get root of A")
@ -353,7 +384,7 @@ func isSameEntries(t *testing.T, a, b *Reader) bool {
return contains(t, aEntry, bEntry) && contains(t, bEntry, aEntry)
}
func compressBlob(t *testing.T, src *io.SectionReader, srcCompression int) *io.SectionReader {
func compressBlob(t TestingT, src *io.SectionReader, srcCompression int) *io.SectionReader {
buf := new(bytes.Buffer)
var w io.WriteCloser
var err error
@ -387,7 +418,7 @@ type stargzEntry struct {
// contains checks if all child entries in "b" are also contained in "a".
// This function also checks if the files/chunks contain the same contents among "a" and "b".
func contains(t *testing.T, a, b stargzEntry) bool {
func contains(t TestingT, a, b stargzEntry) bool {
ae, ar := a.e, a.r
be, br := b.e, b.r
t.Logf("Comparing: %q vs %q", ae.Name, be.Name)
@ -498,7 +529,7 @@ func equalEntry(a, b *TOCEntry) bool {
a.Digest == b.Digest
}
func readOffset(t *testing.T, r *io.SectionReader, offset int64, e stargzEntry) ([]byte, int64, bool) {
func readOffset(t TestingT, r *io.SectionReader, offset int64, e stargzEntry) ([]byte, int64, bool) {
ce, ok := e.r.ChunkEntryForOffset(e.e.Name, offset)
if !ok {
return nil, 0, false
@ -517,7 +548,7 @@ func readOffset(t *testing.T, r *io.SectionReader, offset int64, e stargzEntry)
return data[:n], offset + ce.ChunkSize, true
}
func dumpTOCJSON(t *testing.T, tocJSON *JTOC) string {
func dumpTOCJSON(t TestingT, tocJSON *JTOC) string {
jtocData, err := json.Marshal(*tocJSON)
if err != nil {
t.Fatalf("failed to marshal TOC JSON: %v", err)
@ -531,20 +562,19 @@ func dumpTOCJSON(t *testing.T, tocJSON *JTOC) string {
const chunkSize = 3
// type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, compressionLevel int)
type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory)
type check func(t *TestRunner, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory)
// testDigestAndVerify runs specified checks against sample stargz blobs.
func testDigestAndVerify(t *testing.T, controllers ...TestingControllerFactory) {
func testDigestAndVerify(t *TestRunner, controllers ...TestingControllerFactory) {
tests := []struct {
name string
tarInit func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry)
tarInit func(t TestingT, dgstMap map[string]digest.Digest) (blob []tarEntry)
checks []check
minChunkSize []int
}{
{
name: "no-regfile",
tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) {
tarInit: func(t TestingT, dgstMap map[string]digest.Digest) (blob []tarEntry) {
return tarOf(
dir("test/"),
)
@ -559,7 +589,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingControllerFactory)
},
{
name: "small-files",
tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) {
tarInit: func(t TestingT, dgstMap map[string]digest.Digest) (blob []tarEntry) {
return tarOf(
regDigest(t, "baz.txt", "", dgstMap),
regDigest(t, "foo.txt", "a", dgstMap),
@ -583,7 +613,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingControllerFactory)
},
{
name: "big-files",
tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) {
tarInit: func(t TestingT, dgstMap map[string]digest.Digest) (blob []tarEntry) {
return tarOf(
regDigest(t, "baz.txt", "bazbazbazbazbazbazbaz", dgstMap),
regDigest(t, "foo.txt", "a", dgstMap),
@ -607,7 +637,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingControllerFactory)
{
name: "with-non-regfiles",
minChunkSize: []int{0, 64000},
tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) {
tarInit: func(t TestingT, dgstMap map[string]digest.Digest) (blob []tarEntry) {
return tarOf(
regDigest(t, "baz.txt", "bazbazbazbazbazbazbaz", dgstMap),
regDigest(t, "foo.txt", "a", dgstMap),
@ -654,7 +684,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingControllerFactory)
srcTarFormat := srcTarFormat
for _, minChunkSize := range tt.minChunkSize {
minChunkSize := minChunkSize
t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,format=%s,minChunkSize=%d", newCL(), prefix, srcTarFormat, minChunkSize), func(t *testing.T) {
t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,format=%s,minChunkSize=%d", newCL(), prefix, srcTarFormat, minChunkSize), func(t *TestRunner) {
// Get original tar file and chunk digests
dgstMap := make(map[string]digest.Digest)
tarBlob := buildTar(t, tt.tarInit(t, dgstMap), prefix, srcTarFormat)
@ -690,7 +720,7 @@ func testDigestAndVerify(t *testing.T, controllers ...TestingControllerFactory)
// checkStargzTOC checks the TOC JSON of the passed stargz has the expected
// digest and contains valid chunks. It walks all entries in the stargz and
// checks all chunk digests stored to the TOC JSON match the actual contents.
func checkStargzTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
func checkStargzTOC(t *TestRunner, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
sgz, err := Open(
io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))),
WithDecompressors(controller),
@ -801,7 +831,7 @@ func checkStargzTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstM
// checkVerifyTOC checks the verification works for the TOC JSON of the passed
// stargz. It walks all entries in the stargz and checks the verifications for
// all chunks work.
func checkVerifyTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
func checkVerifyTOC(t *TestRunner, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
sgz, err := Open(
io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))),
WithDecompressors(controller),
@ -882,9 +912,9 @@ func checkVerifyTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstM
// checkVerifyInvalidTOCEntryFail checks if misconfigured TOC JSON can be
// detected during the verification and the verification returns an error.
func checkVerifyInvalidTOCEntryFail(filename string) check {
return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
return func(t *TestRunner, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
funcs := map[string]rewriteFunc{
"lost digest in a entry": func(t *testing.T, toc *JTOC, sgz *io.SectionReader) {
"lost digest in a entry": func(t TestingT, toc *JTOC, sgz *io.SectionReader) {
var found bool
for _, e := range toc.Entries {
if cleanEntryName(e.Name) == filename {
@ -902,7 +932,7 @@ func checkVerifyInvalidTOCEntryFail(filename string) check {
t.Fatalf("rewrite target not found")
}
},
"duplicated entry offset": func(t *testing.T, toc *JTOC, sgz *io.SectionReader) {
"duplicated entry offset": func(t TestingT, toc *JTOC, sgz *io.SectionReader) {
var (
sampleEntry *TOCEntry
targetEntry *TOCEntry
@ -929,7 +959,7 @@ func checkVerifyInvalidTOCEntryFail(filename string) check {
}
for name, rFunc := range funcs {
t.Run(name, func(t *testing.T) {
t.Run(name, func(t *TestRunner) {
newSgz, newTocDigest := rewriteTOCJSON(t, io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), rFunc, controller)
buf := new(bytes.Buffer)
if _, err := io.Copy(buf, newSgz); err != nil {
@ -958,7 +988,7 @@ func checkVerifyInvalidTOCEntryFail(filename string) check {
// checkVerifyInvalidStargzFail checks if the verification detects that the
// given stargz file doesn't match to the expected digest and returns error.
func checkVerifyInvalidStargzFail(invalid *io.SectionReader) check {
return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
return func(t *TestRunner, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
cl := newController()
rc, err := Build(invalid, WithChunkSize(chunkSize), WithCompression(cl))
if err != nil {
@ -990,7 +1020,7 @@ func checkVerifyInvalidStargzFail(invalid *io.SectionReader) check {
// checkVerifyBrokenContentFail checks if the verifier detects broken contents
// that doesn't match to the expected digest and returns error.
func checkVerifyBrokenContentFail(filename string) check {
return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
return func(t *TestRunner, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
// Parse stargz file
sgz, err := Open(
io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))),
@ -1047,9 +1077,9 @@ func chunkID(name string, offset, size int64) string {
return fmt.Sprintf("%s-%d-%d", cleanEntryName(name), offset, size)
}
type rewriteFunc func(t *testing.T, toc *JTOC, sgz *io.SectionReader)
type rewriteFunc func(t TestingT, toc *JTOC, sgz *io.SectionReader)
func rewriteTOCJSON(t *testing.T, sgz *io.SectionReader, rewrite rewriteFunc, controller TestingController) (newSgz io.Reader, tocDigest digest.Digest) {
func rewriteTOCJSON(t TestingT, sgz *io.SectionReader, rewrite rewriteFunc, controller TestingController) (newSgz io.Reader, tocDigest digest.Digest) {
decodedJTOC, jtocOffset, err := parseStargz(sgz, controller)
if err != nil {
t.Fatalf("failed to extract TOC JSON: %v", err)
@ -1120,7 +1150,7 @@ func parseStargz(sgz *io.SectionReader, controller TestingController) (decodedJT
return decodedJTOC, tocOffset, nil
}
func testWriteAndOpen(t *testing.T, controllers ...TestingControllerFactory) {
func testWriteAndOpen(t *TestRunner, controllers ...TestingControllerFactory) {
const content = "Some contents"
invalidUtf8 := "\xff\xfe\xfd"
@ -1464,7 +1494,7 @@ func testWriteAndOpen(t *testing.T, controllers ...TestingControllerFactory) {
for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} {
srcTarFormat := srcTarFormat
for _, lossless := range []bool{true, false} {
t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,lossless=%v,format=%s", newCL(), prefix, lossless, srcTarFormat), func(t *testing.T) {
t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,lossless=%v,format=%s", newCL(), prefix, lossless, srcTarFormat), func(t *TestRunner) {
var tr io.Reader = buildTar(t, tt.in, prefix, srcTarFormat)
origTarDgstr := digest.Canonical.Digester()
tr = io.TeeReader(tr, origTarDgstr.Hash())
@ -1628,7 +1658,7 @@ func digestFor(content string) string {
type numTOCEntries int
func (n numTOCEntries) check(t *testing.T, r *Reader) {
func (n numTOCEntries) check(t TestingT, r *Reader) {
if r.toc == nil {
t.Fatal("nil TOC")
}
@ -1648,15 +1678,15 @@ func (n numTOCEntries) check(t *testing.T, r *Reader) {
func checks(s ...stargzCheck) []stargzCheck { return s }
type stargzCheck interface {
check(t *testing.T, r *Reader)
check(t TestingT, r *Reader)
}
type stargzCheckFn func(*testing.T, *Reader)
type stargzCheckFn func(TestingT, *Reader)
func (f stargzCheckFn) check(t *testing.T, r *Reader) { f(t, r) }
func (f stargzCheckFn) check(t TestingT, r *Reader) { f(t, r) }
func maxDepth(max int) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
e, ok := r.Lookup("")
if !ok {
t.Fatal("root directory not found")
@ -1673,7 +1703,7 @@ func maxDepth(max int) stargzCheck {
})
}
func getMaxDepth(t *testing.T, e *TOCEntry, current, limit int) (max int, rErr error) {
func getMaxDepth(t TestingT, e *TOCEntry, current, limit int) (max int, rErr error) {
if current > limit {
return -1, fmt.Errorf("walkMaxDepth: exceeds limit: current:%d > limit:%d",
current, limit)
@ -1695,7 +1725,7 @@ func getMaxDepth(t *testing.T, e *TOCEntry, current, limit int) (max int, rErr e
}
func hasFileLen(file string, wantLen int) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
for _, ent := range r.toc.Entries {
if ent.Name == file {
if ent.Type != "reg" {
@ -1711,7 +1741,7 @@ func hasFileLen(file string, wantLen int) stargzCheck {
}
func hasFileXattrs(file, name, value string) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
for _, ent := range r.toc.Entries {
if ent.Name == file {
if ent.Type != "reg" {
@ -1738,7 +1768,7 @@ func hasFileXattrs(file, name, value string) stargzCheck {
}
func hasFileDigest(file string, digest string) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
ent, ok := r.Lookup(file)
if !ok {
t.Fatalf("didn't find TOCEntry for file %q", file)
@ -1750,7 +1780,7 @@ func hasFileDigest(file string, digest string) stargzCheck {
}
func hasFileContentsWithPreRead(file string, offset int, want string, extra ...chunkInfo) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
extraMap := make(map[string]chunkInfo)
for _, e := range extra {
extraMap[e.name] = e
@ -1797,7 +1827,7 @@ func hasFileContentsWithPreRead(file string, offset int, want string, extra ...c
}
func hasFileContentsRange(file string, offset int, want string) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
f, err := r.OpenFile(file)
if err != nil {
t.Fatal(err)
@ -1814,7 +1844,7 @@ func hasFileContentsRange(file string, offset int, want string) stargzCheck {
}
func hasChunkEntries(file string, wantChunks int) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
ent, ok := r.Lookup(file)
if !ok {
t.Fatalf("no file for %q", file)
@ -1858,7 +1888,7 @@ func hasChunkEntries(file string, wantChunks int) stargzCheck {
}
func entryHasChildren(dir string, want ...string) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
want := append([]string(nil), want...)
var got []string
ent, ok := r.Lookup(dir)
@ -1877,7 +1907,7 @@ func entryHasChildren(dir string, want ...string) stargzCheck {
}
func hasDir(file string) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
for _, ent := range r.toc.Entries {
if ent.Name == cleanEntryName(file) {
if ent.Type != "dir" {
@ -1891,7 +1921,7 @@ func hasDir(file string) stargzCheck {
}
func hasDirLinkCount(file string, count int) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
for _, ent := range r.toc.Entries {
if ent.Name == cleanEntryName(file) {
if ent.Type != "dir" {
@ -1909,7 +1939,7 @@ func hasDirLinkCount(file string, count int) stargzCheck {
}
func hasMode(file string, mode os.FileMode) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
for _, ent := range r.toc.Entries {
if ent.Name == cleanEntryName(file) {
if ent.Stat().Mode() != mode {
@ -1924,7 +1954,7 @@ func hasMode(file string, mode os.FileMode) stargzCheck {
}
func hasSymlink(file, target string) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
for _, ent := range r.toc.Entries {
if ent.Name == file {
if ent.Type != "symlink" {
@ -1940,7 +1970,7 @@ func hasSymlink(file, target string) stargzCheck {
}
func lookupMatch(name string, want *TOCEntry) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
e, ok := r.Lookup(name)
if !ok {
t.Fatalf("failed to Lookup entry %q", name)
@ -1953,7 +1983,7 @@ func lookupMatch(name string, want *TOCEntry) stargzCheck {
}
func hasEntryOwner(entry string, owner owner) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
ent, ok := r.Lookup(strings.TrimSuffix(entry, "/"))
if !ok {
t.Errorf("entry %q not found", entry)
@ -1967,7 +1997,7 @@ func hasEntryOwner(entry string, owner owner) stargzCheck {
}
func mustSameEntry(files ...string) stargzCheck {
return stargzCheckFn(func(t *testing.T, r *Reader) {
return stargzCheckFn(func(t TestingT, r *Reader) {
var first *TOCEntry
for _, f := range files {
if first == nil {
@ -2039,7 +2069,7 @@ func (f tarEntryFunc) appendTar(tw *tar.Writer, prefix string, format tar.Format
return f(tw, prefix, format)
}
func buildTar(t *testing.T, ents []tarEntry, prefix string, opts ...interface{}) *io.SectionReader {
func buildTar(t TestingT, ents []tarEntry, prefix string, opts ...interface{}) *io.SectionReader {
format := tar.FormatUnknown
for _, opt := range opts {
switch v := opt.(type) {
@ -2248,7 +2278,7 @@ func noPrefetchLandmark() tarEntry {
})
}
func regDigest(t *testing.T, name string, contentStr string, digestMap map[string]digest.Digest) tarEntry {
func regDigest(t TestingT, name string, contentStr string, digestMap map[string]digest.Digest) tarEntry {
if digestMap == nil {
t.Fatalf("digest map mustn't be nil")
}
@ -2318,7 +2348,7 @@ func (f fileInfoOnlyMode) ModTime() time.Time { return time.Now() }
func (f fileInfoOnlyMode) IsDir() bool { return os.FileMode(f).IsDir() }
func (f fileInfoOnlyMode) Sys() interface{} { return nil }
func CheckGzipHasStreams(t *testing.T, b []byte, streams []int64) {
func CheckGzipHasStreams(t TestingT, b []byte, streams []int64) {
if len(streams) == 0 {
return // nop
}
@ -2356,7 +2386,7 @@ func CheckGzipHasStreams(t *testing.T, b []byte, streams []int64) {
}
}
func GzipDiffIDOf(t *testing.T, b []byte) string {
func GzipDiffIDOf(t TestingT, b []byte) string {
h := sha256.New()
zr, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {

View File

@ -30,7 +30,22 @@ import (
// TestZstdChunked tests zstd:chunked
func TestZstdChunked(t *testing.T) {
estargz.CompressionTestSuite(t,
testRunner := &estargz.TestRunner{
TestingT: t,
Runner: func(testingT estargz.TestingT, name string, run func(t estargz.TestingT)) {
tt, ok := testingT.(*testing.T)
if !ok {
testingT.Fatal("TestingT is not a *testing.T")
return
}
tt.Run(name, func(t *testing.T) {
run(t)
})
},
}
estargz.CompressionTestSuite(testRunner,
zstdControllerWithLevel(zstd.SpeedFastest),
zstdControllerWithLevel(zstd.SpeedDefault),
zstdControllerWithLevel(zstd.SpeedBetterCompression),
@ -55,7 +70,7 @@ func (zc *zstdController) String() string {
// TestStream tests the passed zstdchunked blob contains the specified list of streams.
// The last entry of streams must be the offset of footer (len(b) - footerSize).
func (zc *zstdController) TestStreams(t *testing.T, b []byte, streams []int64) {
func (zc *zstdController) TestStreams(t estargz.TestingT, b []byte, streams []int64) {
t.Logf("got zstd streams (compressed size: %d):", len(b))
if len(streams) == 0 {
@ -122,7 +137,7 @@ func nextIndex(s1, sub []byte) int {
return -1
}
func (zc *zstdController) DiffIDOf(t *testing.T, b []byte) string {
func (zc *zstdController) DiffIDOf(t estargz.TestingT, b []byte) string {
h := sha256.New()
zr, err := zstd.NewReader(bytes.NewReader(b))
if err != nil {

View File

@ -139,6 +139,9 @@ type DirectoryCacheConfig struct {
// Direct disables on-memory data cache. Default is true for saving memory usage.
Direct bool `toml:"direct" default:"true" json:"direct"`
// FadvDontNeed forcefully clean fscache pagecache for saving memory. Default is false.
FadvDontNeed bool `toml:"fadv_dontneed" json:"fadv_dontneed"`
}
// FuseConfig is configuration for FUSE fs.

View File

@ -240,6 +240,7 @@ func newCache(root string, cacheType string, cfg config.Config) (cache.BlobCache
FdCache: fCache,
BufPool: bufPool,
Direct: dcc.Direct,
FadvDontNeed: dcc.FadvDontNeed,
},
)
}

View File

@ -30,7 +30,22 @@ import (
)
func TestLayer(t *testing.T) {
TestSuiteLayer(t, memorymetadata.NewReader)
testRunner := &TestRunner{
TestingT: t,
Runner: func(testingT TestingT, name string, run func(t TestingT)) {
tt, ok := testingT.(*testing.T)
if !ok {
testingT.Fatal("TestingT is not a *testing.T")
return
}
tt.Run(name, func(t *testing.T) {
run(t)
})
},
}
TestSuiteLayer(testRunner, memorymetadata.NewReader)
}
func TestWaiter(t *testing.T) {

View File

@ -39,7 +39,6 @@ import (
"path/filepath"
"strings"
"syscall"
"testing"
"time"
"github.com/containerd/containerd/v2/pkg/reference"
@ -76,7 +75,33 @@ type layerConfig struct {
passThroughConfig passThroughConfig
}
func TestSuiteLayer(t *testing.T, store metadata.Store) {
// TestingT is the minimal set of testing.T required to run the
// tests defined in TestSuiteLayer. This interface exists to prevent
// leaking the testing package from being exposed outside tests.
type TestingT interface {
Errorf(format string, args ...any)
Fatal(args ...any)
Fatalf(format string, args ...any)
Logf(format string, args ...any)
}
// Runner allows running subtests of TestingT. This exists instead of adding
// a Run method to TestingT interface because the Run implementation of
// testing.T would not satisfy the interface.
type Runner func(t TestingT, name string, fn func(t TestingT))
type TestRunner struct {
TestingT
Runner Runner
}
func (r *TestRunner) Run(name string, run func(*TestRunner)) {
r.Runner(r.TestingT, name, func(t TestingT) {
run(&TestRunner{TestingT: t, Runner: r.Runner})
})
}
func TestSuiteLayer(t *TestRunner, store metadata.Store) {
for _, lc := range []layerConfig{
{
name: "default",
@ -117,10 +142,14 @@ func TestSuiteLayer(t *testing.T, store metadata.Store) {
var testStateLayerDigest = digest.FromString("dummy")
func testPrefetch(t *testing.T, factory metadata.Store, lc layerConfig) {
data64KB := string(tutil.RandomBytes(t, 64000))
func testPrefetch(t *TestRunner, factory metadata.Store, lc layerConfig) {
randomData, err := tutil.RandomBytes(64000)
if err != nil {
t.Fatalf("failed rand.Read: %v", err)
}
data64KB := string(randomData)
defaultPrefetchSize := int64(10000)
landmarkPosition := func(t *testing.T, l *layer) int64 {
landmarkPosition := func(t TestingT, l *layer) int64 {
if l.r == nil {
t.Fatalf("layer hasn't been verified yet")
}
@ -140,7 +169,7 @@ func testPrefetch(t *testing.T, factory metadata.Store, lc layerConfig) {
in []tutil.TarEntry
wantNum int // number of chunks wanted in the cache
wants []string // filenames to compare
prefetchSize func(*testing.T, *layer) int64
prefetchSize func(TestingT, *layer) int64
prioritizedFiles []string
}{
{
@ -218,7 +247,7 @@ func testPrefetch(t *testing.T, factory metadata.Store, lc layerConfig) {
for _, tt := range tests {
for srcCompressionName, srcCompression := range srcCompressions {
cl := srcCompression()
t.Run("testPrefetch-"+tt.name+"-"+srcCompressionName+"-"+lc.name, func(t *testing.T) {
t.Run("testPrefetch-"+tt.name+"-"+srcCompressionName+"-"+lc.name, func(t *TestRunner) {
chunkSize := sampleChunkSize
if tt.chunkSize > 0 {
chunkSize = tt.chunkSize
@ -345,7 +374,7 @@ func isDup(a, b region) bool {
return b.end >= a.begin
}
func newBlob(t *testing.T, sr *io.SectionReader) *sampleBlob {
func newBlob(t TestingT, sr *io.SectionReader) *sampleBlob {
return &sampleBlob{
t: t,
r: sr,
@ -353,7 +382,7 @@ func newBlob(t *testing.T, sr *io.SectionReader) *sampleBlob {
}
type sampleBlob struct {
t *testing.T
t TestingT
r *io.SectionReader
readCalled bool
@ -418,7 +447,7 @@ const (
lastChunkOffset1 = sampleChunkSize * (int64(len(sampleData1)) / sampleChunkSize)
)
func testNodeRead(t *testing.T, factory metadata.Store, lc layerConfig) {
func testNodeRead(t *TestRunner, factory metadata.Store, lc layerConfig) {
sizeCond := map[string]int64{
"single_chunk": sampleChunkSize - sampleMiddleOffset,
"multi_chunks": sampleChunkSize + sampleMiddleOffset,
@ -443,7 +472,7 @@ func testNodeRead(t *testing.T, factory metadata.Store, lc layerConfig) {
for fn, filesize := range fileSizeCond {
for _, srcCompression := range srcCompressions {
cl := srcCompression()
t.Run(fmt.Sprintf("reading_%s_%s_%s_%s_%s", sn, in, bo, fn, lc.name), func(t *testing.T) {
t.Run(fmt.Sprintf("reading_%s_%s_%s_%s_%s", sn, in, bo, fn, lc.name), func(t *TestRunner) {
if filesize > int64(len(sampleData1)) {
t.Fatal("sample file size is larger than sample data")
}
@ -498,7 +527,7 @@ func testNodeRead(t *testing.T, factory metadata.Store, lc layerConfig) {
}
}
func makeNodeReader(t *testing.T, contents []byte, chunkSize int, factory metadata.Store, cl tutil.Compression, lc layerConfig) (_ *file, closeFn func() error) {
func makeNodeReader(t TestingT, contents []byte, chunkSize int, factory metadata.Store, cl tutil.Compression, lc layerConfig) (_ *file, closeFn func() error) {
testName := "test"
sr, tocDgst, err := tutil.BuildEStargz(
[]tutil.TarEntry{tutil.File(testName, string(contents))},
@ -526,16 +555,20 @@ func makeNodeReader(t *testing.T, contents []byte, chunkSize int, factory metada
return f.(*file), r.Close
}
func testNodes(t *testing.T, factory metadata.Store, lc layerConfig) {
func testNodes(t *TestRunner, factory metadata.Store, lc layerConfig) {
for _, o := range []OverlayOpaqueType{OverlayOpaqueAll, OverlayOpaqueTrusted, OverlayOpaqueUser} {
testNodesWithOpaque(t, factory, o, lc)
}
}
func testNodesWithOpaque(t *testing.T, factory metadata.Store, opaque OverlayOpaqueType, lc layerConfig) {
data64KB := string(tutil.RandomBytes(t, 64000))
func testNodesWithOpaque(t *TestRunner, factory metadata.Store, opaque OverlayOpaqueType, lc layerConfig) {
randomData, err := tutil.RandomBytes(64000)
if err != nil {
t.Fatalf("failed rand.Read: %v", err)
}
data64KB := string(randomData)
hasOpaque := func(entry string) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
for _, k := range opaqueXattrs[opaque] {
hasNodeXattrs(entry, k, opaqueXattrValue)(t, root, cc, cr)
}
@ -741,7 +774,7 @@ func testNodesWithOpaque(t *testing.T, factory metadata.Store, opaque OverlayOpa
for _, tt := range tests {
for _, srcCompression := range srcCompressions {
cl := srcCompression()
t.Run(tt.name+"-"+lc.name, func(t *testing.T) {
t.Run(tt.name+"-"+lc.name, func(t *TestRunner) {
opts := []tutil.BuildEStargzOption{
tutil.WithEStargzOptions(estargz.WithCompression(cl)),
}
@ -772,7 +805,7 @@ func testNodesWithOpaque(t *testing.T, factory metadata.Store, opaque OverlayOpa
}
}
func getRootNode(t *testing.T, r metadata.Reader, opaque OverlayOpaqueType, tocDgst digest.Digest, cc cache.BlobCache, lc layerConfig) *node {
func getRootNode(t TestingT, r metadata.Reader, opaque OverlayOpaqueType, tocDgst digest.Digest, cc cache.BlobCache, lc layerConfig) *node {
vr, err := reader.NewReader(r, cc, digest.FromString(""))
if err != nil {
t.Fatalf("failed to create reader: %v", err)
@ -806,10 +839,10 @@ func (tb *testBlobState) Refresh(ctx context.Context, host source.RegistryHosts,
}
func (tb *testBlobState) Close() error { return nil }
type check func(*testing.T, *node, cache.BlobCache, *calledReaderAt)
type check func(TestingT, *node, cache.BlobCache, *calledReaderAt)
func fileNotExist(file string) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
if _, _, err := getDirentAndNode(t, root, file); err == nil {
t.Errorf("Node %q exists", file)
}
@ -817,7 +850,7 @@ func fileNotExist(file string) check {
}
func hasFileDigest(filename string, digest string) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
_, n, err := getDirentAndNode(t, root, filename)
if err != nil {
t.Fatalf("failed to get node %q: %v", filename, err)
@ -846,7 +879,7 @@ func hasFileDigest(filename string, digest string) check {
}
func hasSize(name string, size int) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
_, n, err := getDirentAndNode(t, root, name)
if err != nil {
t.Fatalf("failed to get node %q: %v", name, err)
@ -862,7 +895,7 @@ func hasSize(name string, size int) check {
}
func hasExtraMode(name string, mode os.FileMode) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
_, n, err := getDirentAndNode(t, root, name)
if err != nil {
t.Fatalf("failed to get node %q: %v", name, err)
@ -881,7 +914,7 @@ func hasExtraMode(name string, mode os.FileMode) check {
}
func hasValidWhiteout(name string) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
ent, n, err := getDirentAndNode(t, root, name)
if err != nil {
t.Fatalf("failed to get node %q: %v", name, err)
@ -917,7 +950,7 @@ func hasValidWhiteout(name string) check {
}
func hasNodeXattrs(entry, name, value string) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
_, n, err := getDirentAndNode(t, root, entry)
if err != nil {
t.Fatalf("failed to get node %q: %v", entry, err)
@ -958,7 +991,7 @@ func hasNodeXattrs(entry, name, value string) check {
}
}
func hasEntry(t *testing.T, name string, ents fusefs.DirStream) (fuse.DirEntry, bool) {
func hasEntry(t TestingT, name string, ents fusefs.DirStream) (fuse.DirEntry, bool) {
for ents.HasNext() {
de, errno := ents.Next()
if errno != 0 {
@ -971,8 +1004,8 @@ func hasEntry(t *testing.T, name string, ents fusefs.DirStream) (fuse.DirEntry,
return fuse.DirEntry{}, false
}
func hasStateFile(t *testing.T, id string) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
func hasStateFile(t TestingT, id string) check {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
// Check the state dir is hidden on OpenDir for "/"
ents, errno := root.Readdir(context.Background())
@ -1069,7 +1102,7 @@ func hasStateFile(t *testing.T, id string) check {
// getDirentAndNode gets dirent and node at the specified path at once and makes
// sure that the both of them exist.
func getDirentAndNode(t *testing.T, root *node, path string) (ent fuse.DirEntry, n *fusefs.Inode, err error) {
func getDirentAndNode(t TestingT, root *node, path string) (ent fuse.DirEntry, n *fusefs.Inode, err error) {
dir, base := filepath.Split(filepath.Clean(path))
// get the target's parent directory.
@ -1145,7 +1178,7 @@ type chunkInfo struct {
}
func hasFileContentsWithPreCached(name string, off int64, contents string, extra ...chunkInfo) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
buf := readFile(t, root, name, int64(len(contents)), off)
if len(buf) != len(contents) {
t.Fatalf("failed to read contents %q (off:%d, want:%q) got %q", name, off, longBytesView([]byte(contents)), longBytesView(buf))
@ -1167,7 +1200,7 @@ func hasFileContentsWithPreCached(name string, off int64, contents string, extra
}
func hasFileContentsOffset(name string, off int64, contents string, fromCache bool) check {
return func(t *testing.T, root *node, cc cache.BlobCache, cr *calledReaderAt) {
return func(t TestingT, root *node, cc cache.BlobCache, cr *calledReaderAt) {
cr.called = nil // reset test
buf := readFile(t, root, name, int64(len(contents)), off)
if len(buf) != len(contents) {
@ -1189,7 +1222,7 @@ func hasFileContentsOffset(name string, off int64, contents string, fromCache bo
}
}
func readFile(t *testing.T, root *node, filename string, size, off int64) []byte {
func readFile(t TestingT, root *node, filename string, size, off int64) []byte {
_, n, err := getDirentAndNode(t, root, filename)
if err != nil {
t.Fatalf("failed to get node %q: %v", filename, err)

View File

@ -29,5 +29,20 @@ import (
)
func TestReader(t *testing.T) {
TestSuiteReader(t, memorymetadata.NewReader)
testRunner := &TestRunner{
TestingT: t,
Runner: func(testingT TestingT, name string, run func(t TestingT)) {
tt, ok := testingT.(*testing.T)
if !ok {
testingT.Fatal("TestingT is not a *testing.T")
return
}
tt.Run(name, func(t *testing.T) {
run(t)
})
},
}
TestSuiteReader(testRunner, memorymetadata.NewReader)
}

View File

@ -32,7 +32,6 @@ import (
"path/filepath"
"strings"
"sync"
"testing"
"time"
"github.com/containerd/stargz-snapshotter/cache"
@ -64,7 +63,34 @@ var (
MockReadAtOutput = 4194304
)
func TestSuiteReader(t *testing.T, store metadata.Store) {
// TestingT is the minimal set of testing.T required to run the
// tests defined in TestSuiteReader. This interface exists to prevent
// leaking the testing package from being exposed outside tests.
type TestingT interface {
Cleanup(func())
Errorf(format string, args ...any)
Fatal(args ...any)
Fatalf(format string, args ...any)
Logf(format string, args ...any)
}
// Runner allows running subtests of TestingT. This exists instead of adding
// a Run method to TestingT interface because the Run implementation of
// testing.T would not satisfy the interface.
type Runner func(t TestingT, name string, fn func(t TestingT))
type TestRunner struct {
TestingT
Runner Runner
}
func (r *TestRunner) Run(name string, run func(*TestRunner)) {
r.Runner(r.TestingT, name, func(t TestingT) {
run(&TestRunner{TestingT: t, Runner: r.Runner})
})
}
func TestSuiteReader(t *TestRunner, store metadata.Store) {
testFileReadAt(t, store)
testCacheVerify(t, store)
testFailReader(t, store)
@ -72,7 +98,7 @@ func TestSuiteReader(t *testing.T, store metadata.Store) {
testProcessBatchChunks(t)
}
func testFileReadAt(t *testing.T, factory metadata.Store) {
func testFileReadAt(t *TestRunner, factory metadata.Store) {
sizeCond := map[string]int64{
"single_chunk": sampleChunkSize - sampleMiddleOffset,
"multi_chunks": sampleChunkSize + sampleMiddleOffset,
@ -109,7 +135,7 @@ func testFileReadAt(t *testing.T, factory metadata.Store) {
for cc, cacheExcept := range cacheCond {
for srcCompressionName, srcCompression := range srcCompressions {
srcCompression := srcCompression()
t.Run(fmt.Sprintf("reading_%s_%s_%s_%s_%s_%s", sn, in, bo, fn, cc, srcCompressionName), func(t *testing.T) {
t.Run(fmt.Sprintf("reading_%s_%s_%s_%s_%s_%s", sn, in, bo, fn, cc, srcCompressionName), func(t *TestRunner) {
if filesize > int64(len(sampleData1)) {
t.Fatal("sample file size is larger than sample data")
}
@ -199,7 +225,7 @@ func testFileReadAt(t *testing.T, factory metadata.Store) {
}
}
func newExceptFile(t *testing.T, fr metadata.File, except ...region) metadata.File {
func newExceptFile(t TestingT, fr metadata.File, except ...region) metadata.File {
er := exceptFile{fr: fr, t: t}
er.except = map[region]bool{}
for _, reg := range except {
@ -211,7 +237,7 @@ func newExceptFile(t *testing.T, fr metadata.File, except ...region) metadata.Fi
type exceptFile struct {
fr metadata.File
except map[region]bool
t *testing.T
t TestingT
}
func (er *exceptFile) ReadAt(p []byte, offset int64) (int, error) {
@ -225,7 +251,7 @@ func (er *exceptFile) ChunkEntryForOffset(offset int64) (off int64, size int64,
return er.fr.ChunkEntryForOffset(offset)
}
func makeFile(t *testing.T, contents []byte, chunkSize int, factory metadata.Store, comp tutil.Compression) (*file, func() error) {
func makeFile(t TestingT, contents []byte, chunkSize int, factory metadata.Store, comp tutil.Compression) (*file, func() error) {
testName := "test"
sr, dgst, err := tutil.BuildEStargz([]tutil.TarEntry{
tutil.File(testName, string(contents)),
@ -265,7 +291,7 @@ func makeFile(t *testing.T, contents []byte, chunkSize int, factory metadata.Sto
return f, vr.Close
}
func testCacheVerify(t *testing.T, factory metadata.Store) {
func testCacheVerify(t *TestRunner, factory metadata.Store) {
for _, skipVerify := range [2]bool{true, false} {
for _, invalidChunkBeforeVerify := range [2]bool{true, false} {
for _, invalidChunkAfterVerify := range [2]bool{true, false} {
@ -273,7 +299,7 @@ func testCacheVerify(t *testing.T, factory metadata.Store) {
srcCompression := srcCompression()
name := fmt.Sprintf("test_cache_verify_%v_%v_%v_%v",
skipVerify, invalidChunkBeforeVerify, invalidChunkAfterVerify, srcCompressionName)
t.Run(name, func(t *testing.T) {
t.Run(name, func(t *TestRunner) {
sr, tocDgst, err := tutil.BuildEStargz([]tutil.TarEntry{
tutil.File("a", sampleData1+"a"),
tutil.File("b", sampleData1+"b"),
@ -483,11 +509,11 @@ func prepareMap(mr metadata.Reader, id uint32, p string) (off2id map[int64]uint3
return off2id, id2path, nil
}
func testFailReader(t *testing.T, factory metadata.Store) {
func testFailReader(t *TestRunner, factory metadata.Store) {
testFileName := "test"
for srcCompressionName, srcCompression := range srcCompressions {
srcCompression := srcCompression()
t.Run(fmt.Sprintf("%v", srcCompressionName), func(t *testing.T) {
t.Run(fmt.Sprintf("%v", srcCompressionName), func(t *TestRunner) {
for _, rs := range []bool{true, false} {
for _, vs := range []bool{true, false} {
stargzFile, tocDigest, err := tutil.BuildEStargz([]tutil.TarEntry{
@ -595,8 +621,12 @@ func (bev *testChunkVerifier) verifier(id uint32, chunkDigest string) (digest.Ve
return &testVerifier{bev.success}, nil
}
func testPreReader(t *testing.T, factory metadata.Store) {
data64KB := string(tutil.RandomBytes(t, 64000))
func testPreReader(t *TestRunner, factory metadata.Store) {
randomData, err := tutil.RandomBytes(64000)
if err != nil {
t.Fatalf("failed rand.Read: %v", err)
}
data64KB := string(randomData)
tests := []struct {
name string
chunkSize int
@ -666,7 +696,7 @@ func testPreReader(t *testing.T, factory metadata.Store) {
for _, tt := range tests {
for srcCompresionName, srcCompression := range srcCompressions {
srcCompression := srcCompression()
t.Run(tt.name+"-"+srcCompresionName, func(t *testing.T) {
t.Run(tt.name+"-"+srcCompresionName, func(t *TestRunner) {
opts := []tutil.BuildEStargzOption{
tutil.WithEStargzOptions(estargz.WithCompression(srcCompression)),
}
@ -705,7 +735,7 @@ func testPreReader(t *testing.T, factory metadata.Store) {
}
}
type check func(*testing.T, *reader, *calledReaderAt)
type check func(TestingT, *reader, *calledReaderAt)
type chunkInfo struct {
name string
@ -715,7 +745,7 @@ type chunkInfo struct {
}
func hasFileContentsOffset(name string, off int64, contents string, fromCache bool) check {
return func(t *testing.T, r *reader, cr *calledReaderAt) {
return func(t TestingT, r *reader, cr *calledReaderAt) {
tid, err := lookup(r, name)
if err != nil {
t.Fatalf("failed to lookup %q", name)
@ -750,7 +780,7 @@ func hasFileContentsOffset(name string, off int64, contents string, fromCache bo
}
func hasFileContentsWithPreCached(name string, off int64, contents string, extra ...chunkInfo) check {
return func(t *testing.T, r *reader, cr *calledReaderAt) {
return func(t TestingT, r *reader, cr *calledReaderAt) {
tid, err := lookup(r, name)
if err != nil {
t.Fatalf("failed to lookup %q", name)
@ -870,7 +900,7 @@ func (f *mockFile) ReadAt(p []byte, offset int64) (int, error) {
return MockReadAtOutput, nil
}
func testProcessBatchChunks(t *testing.T) {
func testProcessBatchChunks(t *TestRunner) {
type testCase struct {
name string
setupMock func()
@ -878,7 +908,7 @@ func testProcessBatchChunks(t *testing.T) {
expectErrorInHoles bool
}
runTest := func(t *testing.T, tc testCase) {
runTest := func(t TestingT, tc testCase) {
if tc.setupMock != nil {
tc.setupMock()
}
@ -1000,7 +1030,7 @@ func testProcessBatchChunks(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Run(tc.name, func(t *TestRunner) {
runTest(t, tc)
})
}

34
go.mod
View File

@ -6,7 +6,7 @@ toolchain go1.24.2
require (
github.com/containerd/console v1.0.5
github.com/containerd/containerd/v2 v2.1.3
github.com/containerd/containerd/v2 v2.1.4
github.com/containerd/continuity v0.4.5
github.com/containerd/errdefs v1.0.0
github.com/containerd/log v0.1.0
@ -14,7 +14,7 @@ require (
github.com/containerd/plugin v1.0.0
github.com/containerd/stargz-snapshotter/estargz v0.17.0
github.com/distribution/reference v0.6.0
github.com/docker/cli v28.3.2+incompatible
github.com/docker/cli v28.3.3+incompatible
github.com/docker/go-metrics v0.0.1
github.com/gogo/protobuf v1.3.2
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
@ -25,13 +25,13 @@ require (
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/opencontainers/runtime-spec v1.2.1
github.com/prometheus/client_golang v1.22.0
github.com/prometheus/client_golang v1.23.0
github.com/rs/xid v1.6.0
github.com/sirupsen/logrus v1.9.3
go.etcd.io/bbolt v1.4.2
golang.org/x/sync v0.16.0
golang.org/x/sys v0.34.0
google.golang.org/grpc v1.73.0
google.golang.org/grpc v1.74.2
k8s.io/api v0.33.3
k8s.io/apimachinery v0.33.3
k8s.io/client-go v0.33.3
@ -47,7 +47,7 @@ require (
github.com/containerd/containerd/api v1.9.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/go-cni v1.1.12 // indirect
github.com/containerd/go-cni v1.1.13 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/containernetworking/cni v1.3.0 // indirect
@ -57,7 +57,7 @@ require (
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
@ -82,9 +82,9 @@ require (
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/spf13/pflag v1.0.6 // indirect
@ -96,15 +96,15 @@ require (
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.28.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/time v0.9.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect

76
go.sum
View File

@ -23,8 +23,8 @@ github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/q
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
github.com/containerd/containerd/v2 v2.1.3 h1:eMD2SLcIQPdMlnlNF6fatlrlRLAeDaiGPGwmRKLZKNs=
github.com/containerd/containerd/v2 v2.1.3/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ=
github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@ -33,8 +33,8 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/containerd/go-cni v1.1.12 h1:wm/5VD/i255hjM4uIZjBRiEQ7y98W9ACy/mHeLi4+94=
github.com/containerd/go-cni v1.1.12/go.mod h1:+jaqRBdtW5faJxj2Qwg1Of7GsV66xcvnCx4mSJtUlxU=
github.com/containerd/go-cni v1.1.13 h1:eFSGOKlhoYNxpJ51KRIMHZNlg5UgocXEIEBGkY7Hnis=
github.com/containerd/go-cni v1.1.13/go.mod h1:nTieub0XDRmvCZ9VI/SBG6PyqT95N4FIhxsauF1vSBI=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
@ -55,8 +55,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY=
github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.3.3+incompatible h1:fp9ZHAr1WWPGdIWBM1b3zLtgCF+83gRdVMTJsUeiyAo=
github.com/docker/cli v28.3.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
@ -77,8 +77,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
@ -212,22 +212,22 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
@ -275,16 +275,16 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -308,11 +308,11 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -332,12 +332,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -359,15 +359,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=

View File

@ -5,10 +5,10 @@ go 1.23.0
toolchain go1.24.1
require (
github.com/containerd/containerd/v2 v2.1.3
github.com/containerd/containerd/v2 v2.1.4
github.com/containerd/platforms v1.0.0-rc.1
github.com/mitchellh/go-homedir v1.1.0
github.com/multiformats/go-multiaddr v0.16.0
github.com/multiformats/go-multiaddr v0.16.1
github.com/opencontainers/image-spec v1.1.1
)
@ -27,7 +27,7 @@ require (
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@ -57,17 +57,17 @@ require (
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
google.golang.org/grpc v1.73.0 // indirect
golang.org/x/text v0.25.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/grpc v1.74.2 // indirect
google.golang.org/protobuf v1.36.6 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
)

View File

@ -13,8 +13,8 @@ github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJ
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
github.com/containerd/containerd/v2 v2.1.3 h1:eMD2SLcIQPdMlnlNF6fatlrlRLAeDaiGPGwmRKLZKNs=
github.com/containerd/containerd/v2 v2.1.3/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ=
github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@ -45,8 +45,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@ -114,8 +114,8 @@ github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYg
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc=
github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0=
github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw=
github.com/multiformats/go-multiaddr v0.16.1/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
@ -162,22 +162,22 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
@ -195,8 +195,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -215,8 +215,8 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -234,15 +234,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=

View File

@ -25,7 +25,21 @@ import (
)
func TestReader(t *testing.T) {
testutil.TestReader(t, readerFactory)
testRunner := &testutil.TestRunner{
TestingT: t,
Runner: func(testingT testutil.TestingT, name string, run func(t testutil.TestingT)) {
tt, ok := testingT.(*testing.T)
if !ok {
testingT.Fatal("TestingT is not a *testing.T")
return
}
tt.Run(name, func(t *testing.T) {
run(t)
})
},
}
testutil.TestReader(testRunner, readerFactory)
}
func readerFactory(sr *io.SectionReader, opts ...metadata.Option) (testutil.TestableReader, error) {

View File

@ -26,7 +26,6 @@ import (
"path/filepath"
"reflect"
"strings"
"testing"
"time"
"github.com/containerd/stargz-snapshotter/estargz"
@ -60,11 +59,41 @@ type TestableReader interface {
NumOfNodes() (i int, _ error)
}
// TestingT is the minimal set of testing.T required to run the
// tests defined in TestReader. This interface exists to prevent
// leaking the testing package from being exposed outside tests.
type TestingT interface {
Errorf(format string, args ...any)
Fatal(args ...any)
Fatalf(format string, args ...any)
Logf(format string, args ...any)
}
// Runner allows running subtests of TestingT. This exists instead of adding
// a Run method to TestingT interface because the Run implementation of
// testing.T would not satisfy the interface.
type Runner func(t TestingT, name string, fn func(t TestingT))
type TestRunner struct {
TestingT
Runner Runner
}
func (r *TestRunner) Run(name string, run func(*TestRunner)) {
r.Runner(r.TestingT, name, func(t TestingT) {
run(&TestRunner{TestingT: t, Runner: r.Runner})
})
}
// TestReader tests Reader returns correct file metadata.
func TestReader(t *testing.T, factory ReaderFactory) {
func TestReader(t *TestRunner, factory ReaderFactory) {
sampleTime := time.Now().Truncate(time.Second)
sampleText := "qwer" + "tyui" + "opas" + "dfgh" + "jk"
data64KB := string(tutil.RandomBytes(t, 64000))
randomData, err := tutil.RandomBytes(64000)
if err != nil {
t.Fatalf("failed rand.Read: %v", err)
}
data64KB := string(randomData)
tests := []struct {
name string
chunkSize int
@ -286,7 +315,8 @@ func TestReader(t *testing.T, factory ReaderFactory) {
prefix := prefix
for srcCompresionName, srcCompression := range srcCompressions {
srcCompression := srcCompression()
t.Run(tt.name+"-"+srcCompresionName, func(t *testing.T) {
t.Run(tt.name+"-"+srcCompresionName, func(t *TestRunner) {
opts := []tutil.BuildEStargzOption{
tutil.WithBuildTarOptions(tutil.WithPrefix(prefix)),
tutil.WithEStargzOptions(estargz.WithCompression(srcCompression)),
@ -343,7 +373,7 @@ func TestReader(t *testing.T, factory ReaderFactory) {
}
}
t.Run("clone-id-stability", func(t *testing.T) {
t.Run("clone-id-stability", func(t *TestRunner) {
var mapEntries func(r TestableReader, id uint32, m map[string]uint32) (map[string]uint32, error)
mapEntries = func(r TestableReader, id uint32, m map[string]uint32) (map[string]uint32, error) {
if m == nil {
@ -426,7 +456,7 @@ func newCalledTelemetry() (telemetry *metadata.Telemetry, check func() error) {
}
}
func dumpNodes(t *testing.T, r TestableReader, id uint32, level int) {
func dumpNodes(t TestingT, r TestableReader, id uint32, level int) {
if err := r.ForeachChild(id, func(name string, id uint32, mode os.FileMode) bool {
ind := ""
for i := 0; i < level; i++ {
@ -440,10 +470,10 @@ func dumpNodes(t *testing.T, r TestableReader, id uint32, level int) {
}
}
type check func(*testing.T, TestableReader)
type check func(TestingT, TestableReader)
func numOfNodes(want int) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
i, err := r.NumOfNodes()
if err != nil {
t.Errorf("num of nodes: %v", err)
@ -455,7 +485,7 @@ func numOfNodes(want int) check {
}
func numOfChunks(name string, num int) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
nr, ok := r.(interface {
NumOfChunks(id uint32) (i int, _ error)
})
@ -479,7 +509,7 @@ func numOfChunks(name string, num int) check {
}
func sameNodes(n string, nodes ...string) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, n)
if err != nil {
t.Errorf("failed to lookup %q: %v", n, err)
@ -499,7 +529,7 @@ func sameNodes(n string, nodes ...string) check {
}
func linkName(name string, linkName string) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("failed to lookup %q: %v", name, err)
@ -522,7 +552,7 @@ func linkName(name string, linkName string) check {
}
func hasNumLink(name string, numLink int) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("failed to lookup %q: %v", name, err)
@ -541,7 +571,7 @@ func hasNumLink(name string, numLink int) check {
}
func hasDirChildren(name string, children ...string) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("failed to lookup %q: %v", name, err)
@ -576,7 +606,7 @@ func hasDirChildren(name string, children ...string) check {
}
func hasChardev(name string, maj, min int) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find chardev %q: %v", name, err)
@ -599,7 +629,7 @@ func hasChardev(name string, maj, min int) check {
}
func hasBlockdev(name string, maj, min int) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find blockdev %q: %v", name, err)
@ -622,7 +652,7 @@ func hasBlockdev(name string, maj, min int) check {
}
func hasFifo(name string) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find blockdev %q: %v", name, err)
@ -641,7 +671,7 @@ func hasFifo(name string) check {
}
func hasFile(name, content string, size int64) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)
@ -686,7 +716,7 @@ type chunkInfo struct {
}
func hasFileContentsWithPreRead(name string, off int64, contents string, extra ...chunkInfo) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
extraMap := make(map[uint32]chunkInfo)
for _, e := range extra {
id, err := lookup(r, e.name)
@ -753,7 +783,7 @@ func hasFileContentsWithPreRead(name string, off int64, contents string, extra .
}
func hasFileContentsOffset(name string, off int64, contents string) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("failed to lookup %q: %v", name, err)
@ -782,7 +812,7 @@ func hasFileContentsOffset(name string, off int64, contents string) check {
}
func hasMode(name string, mode os.FileMode) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)
@ -801,7 +831,7 @@ func hasMode(name string, mode os.FileMode) check {
}
func hasOwner(name string, uid, gid int) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)
@ -820,7 +850,7 @@ func hasOwner(name string, uid, gid int) check {
}
func hasModTime(name string, modTime time.Time) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)
@ -840,7 +870,7 @@ func hasModTime(name string, modTime time.Time) check {
}
func hasXattrs(name string, xattrs map[string]string) check {
return func(t *testing.T, r TestableReader) {
return func(t TestingT, r TestableReader) {
id, err := lookup(r, name)
if err != nil {
t.Errorf("cannot find file %q: %v", name, err)

View File

@ -760,10 +760,13 @@ func (o *snapshotter) restoreRemoteSnapshot(ctx context.Context) error {
if err != nil {
return err
}
if err := os.Mkdir(filepath.Join(o.root, "snapshots", id), 0700); err != nil {
if err := os.Mkdir(filepath.Join(o.root, "snapshots", id), 0700); err != nil && !os.IsExist(err) {
return err
}
return os.Mkdir(o.upperPath(id), 0755)
if err := os.Mkdir(o.upperPath(id), 0755); err != nil && !os.IsExist(err) {
return err
}
return nil
}(); err != nil {
return fmt.Errorf("failed to create remote snapshot directory: %s: %w", info.Name, err)
}

View File

@ -18,14 +18,13 @@ package testutil
import (
"crypto/rand"
"testing"
)
// RandomBytes returns the specified number of random bytes
func RandomBytes(t *testing.T, n int) []byte {
func RandomBytes(n int) ([]byte, error) {
b := make([]byte, n)
if _, err := rand.Read(b); err != nil {
t.Fatalf("failed rand.Read: %v", err)
return nil, err
}
return b
return b, nil
}