Compare commits
279 Commits
estargz/v0
...
main
Author | SHA1 | Date |
---|---|---|
|
554629b4a1 | |
|
a986f8c194 | |
|
38cf9aa57c | |
|
e07c7abb8c | |
|
7b508b1afe | |
|
08272a30af | |
|
d09a4ffa5b | |
|
1227c8e42b | |
|
21fb6a16e4 | |
|
f400f935f7 | |
|
d0487729a1 | |
|
2aef7a6e63 | |
|
18e393af96 | |
|
927075ba7b | |
|
e53cec5266 | |
|
555e167719 | |
|
f72d3aecc8 | |
|
19a5bd5157 | |
|
f62679453f | |
|
17134ee517 | |
|
420a03fa46 | |
|
3aa69eaac5 | |
|
b3743e7916 | |
|
4677721cfc | |
|
9e6b9b72af | |
|
cfa57c95a9 | |
|
24e8a7858a | |
|
6f6aacb044 | |
|
ff4141ab8a | |
|
dfb9f43a00 | |
|
6bffc90072 | |
|
56bb07c852 | |
|
71c6675ef2 | |
|
0aab392f46 | |
|
020d3474b6 | |
|
0270add8e1 | |
|
b4db2b98bc | |
|
993d44a197 | |
|
26ee38d5c5 | |
|
6e65bca4f5 | |
|
32a679f130 | |
|
5be16a5cfe | |
|
7de6607e7f | |
|
dae086b594 | |
|
d33d9332c4 | |
|
75435f7bb4 | |
|
fd445ac572 | |
|
b405072cb1 | |
|
51d84f2656 | |
|
4a050718c1 | |
|
1b3ba8662f | |
|
1e3ef72d1e | |
|
73e08ea280 | |
|
acb3e9007c | |
|
82999f50f7 | |
|
7cbb501deb | |
|
3733c98749 | |
|
5b78e51f73 | |
|
9945490b14 | |
|
64a898a3e3 | |
|
a744b5da80 | |
|
6cd27f9a06 | |
|
5fabcd2907 | |
|
e351aa9af1 | |
|
22f7f7164a | |
|
8fb5e1d8fb | |
|
967f35a72c | |
|
2173abf9dc | |
|
e463a879bc | |
|
26c7c2de60 | |
|
6b395b9c3a | |
|
d826f0a8bb | |
|
b90d85827a | |
|
22a56133de | |
|
5a1b37f051 | |
|
3c021f1403 | |
|
1d2f153bc1 | |
|
20070f8ff0 | |
|
ddf07e9290 | |
|
147ac653e2 | |
|
c577fcf339 | |
|
b1f8fcc115 | |
|
382512efc6 | |
|
7c7d78c4fc | |
|
ba3491a6b6 | |
|
3bb341f386 | |
|
61cec058ec | |
|
fd3e6ce33d | |
|
24432e9f8c | |
|
6956955469 | |
|
31f53f4ce7 | |
|
1ce551bb21 | |
|
4687f78aaf | |
|
a8c7561e92 | |
|
e02c1d9e5d | |
|
dbe654734c | |
|
7a5255aa87 | |
|
a88e80272d | |
|
448993b170 | |
|
0790ac81af | |
|
0b39089460 | |
|
56a5070e13 | |
|
908ead9eb9 | |
|
3b84e3e6cc | |
|
07a4eeab9d | |
|
bf4c6611c1 | |
|
f0b8fe69e1 | |
|
09d0115ee0 | |
|
ddb9c189ba | |
|
a28797ad9e | |
|
93a7332a68 | |
|
597f6736c8 | |
|
4100e57e56 | |
|
e9c84ab69e | |
|
521cf2357b | |
|
c866f48a91 | |
|
476cca1bb9 | |
|
9bc4c0a90f | |
|
64e2f1ca01 | |
|
d32e99f051 | |
|
bd6fbed40e | |
|
0695afcf9d | |
|
1cb2cbd87a | |
|
2f71f9ea81 | |
|
f78cf735f1 | |
|
20617610f3 | |
|
14d4229cc2 | |
|
04aa66acfe | |
|
ee9a70f65b | |
|
21da82e333 | |
|
ff6d93ffe7 | |
|
5700835dc7 | |
|
571126430b | |
|
f64abb664e | |
|
acf9e82e65 | |
|
91c7a24718 | |
|
54ff30096c | |
|
3d42bd31e1 | |
|
2b82fff1e2 | |
|
dff21ff05b | |
|
72dd8cb40c | |
|
1c4bf94471 | |
|
6d55471940 | |
|
01f4d46344 | |
|
a9a70aa205 | |
|
dbddc6d7d0 | |
|
83235b43f4 | |
|
e31f542702 | |
|
55a5b1e9fa | |
|
fb3d66a8c6 | |
|
6e4d8fe26e | |
|
95d84dc49d | |
|
3d4636757c | |
|
a00b4ba202 | |
|
c249f3b57d | |
|
eb8709ad22 | |
|
89dbb7ee26 | |
|
c60239ad5e | |
|
46b695d108 | |
|
1052f2b3ca | |
|
485286846f | |
|
c09b2ca9cd | |
|
930e944ecb | |
|
b20254636f | |
|
9133e6d667 | |
|
e2cdd6b36a | |
|
2cd3dc452b | |
|
a215137b6f | |
|
4e5823b998 | |
|
d924a271df | |
|
ca7d26e418 | |
|
f75681d180 | |
|
0bbee9a5f2 | |
|
e0dbe544bf | |
|
65f684de6a | |
|
6c371c72dd | |
|
39a7cc58b3 | |
|
0f84891c0a | |
|
4441cbd94a | |
|
f839d37b91 | |
|
3c943351d9 | |
|
921268fad5 | |
|
56a5b82e12 | |
|
bef7113118 | |
|
90f332866b | |
|
6378393321 | |
|
70195cecd2 | |
|
d5f314b218 | |
|
6d1ee71694 | |
|
20920f031e | |
|
16ebb73438 | |
|
db01164bda | |
|
c4e10c6e1d | |
|
d270507e62 | |
|
5fcb52981f | |
|
9fbffbc209 | |
|
0aeda7f1b4 | |
|
8441124370 | |
|
b07768ff1c | |
|
bfb6cf34c4 | |
|
d925291d90 | |
|
1281fc2cd2 | |
|
cabe48b136 | |
|
38aad330e7 | |
|
1645a0eb29 | |
|
f13f534c8a | |
|
d1083271a4 | |
|
a76b90e438 | |
|
051fad67f9 | |
|
740078d5d4 | |
|
b4b4cf9437 | |
|
9d29bf446a | |
|
e20fe9f673 | |
|
4a2a710409 | |
|
6c40179c93 | |
|
e1de8d4da6 | |
|
c309defb0e | |
|
b05c75e8c9 | |
|
9d2951d82c | |
|
8c9ecb7cd3 | |
|
723f610ce0 | |
|
c665b3a9a7 | |
|
26081a6bb6 | |
|
32573d2947 | |
|
8f249479bb | |
|
7db3c42ff0 | |
|
37a436d50c | |
|
5e3411769a | |
|
cc90435e1b | |
|
0e94caea39 | |
|
a17a103f00 | |
|
f7eaa739b0 | |
|
466a8f6e60 | |
|
0aebed85a2 | |
|
e61da34ad6 | |
|
620cbe15fd | |
|
b8a2960e50 | |
|
6b0021f2b4 | |
|
331932408a | |
|
928a4dd771 | |
|
5555c06cb0 | |
|
61ed6ff62c | |
|
3cbe35f1c9 | |
|
24020d4d09 | |
|
db7c74e0d2 | |
|
054b5aee83 | |
|
fbdc6d36e2 | |
|
d8470f9f90 | |
|
d81ec67409 | |
|
394fdcd073 | |
|
a1c6c6f7ef | |
|
9241431348 | |
|
c424276509 | |
|
7efeccc83d | |
|
8556da2ddf | |
|
4ce034ffdf | |
|
4e10317ed8 | |
|
ce7fc7b103 | |
|
ff392c138a | |
|
02793c182d | |
|
61a30ba662 | |
|
a24c5ab6e1 | |
|
06052fde24 | |
|
49aba3abb4 | |
|
a6894ec3fb | |
|
9b54e47bdd | |
|
56c7b0f27b | |
|
1da63ca0fd | |
|
d80a847063 | |
|
efb9325299 | |
|
a0de1966c9 | |
|
1ce6fa7480 | |
|
bbd984f9f0 | |
|
2a280d6b2b | |
|
d16d0655fd | |
|
39a2e55a8b | |
|
71930d7767 | |
|
946a04dda7 | |
|
dad51ec506 |
|
@ -3,30 +3,43 @@ updates:
|
|||
|
||||
# Automatic upgrade for go modules.
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
directories:
|
||||
- "/estargz"
|
||||
- "/ipfs"
|
||||
- "/"
|
||||
- "/cmd"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
# We upgrade this manually on each release
|
||||
- dependency-name: "github.com/containerd/stargz-snapshotter/estargz"
|
||||
|
||||
# Automatic upgrade for go modules of estargz package.
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/estargz"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
# Automatic upgrade for go modules of ipfs package.
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/ipfs"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
# Automatic upgrade for go modules of cmd package.
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/cmd"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
golang-x:
|
||||
patterns:
|
||||
- "golang.org/x/*"
|
||||
google-golang:
|
||||
patterns:
|
||||
- "google.golang.org/*"
|
||||
containerd:
|
||||
patterns:
|
||||
- "github.com/containerd/*"
|
||||
opencontainers:
|
||||
patterns:
|
||||
- "github.com/opencontainers/*"
|
||||
k8s:
|
||||
patterns:
|
||||
- "k8s.io/*"
|
||||
gomod:
|
||||
# this pattern covers all go dependencies that are not in
|
||||
# the above groups. dependabot doesn't seem to update sub-modules if
|
||||
# a dependency doesn't belong to a group, so we define this group
|
||||
# explicitly.
|
||||
exclude-patterns:
|
||||
- "golang.org/x/*"
|
||||
- "google.golang.org/*"
|
||||
- "github.com/containerd/*"
|
||||
- "github.com/opencontainers/*"
|
||||
- "k8s.io/*"
|
||||
|
||||
# Automatic upgrade for base images used in the Dockerfile
|
||||
- package-ecosystem: "docker"
|
||||
|
|
|
@ -3,6 +3,7 @@ on:
|
|||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
|
@ -32,10 +33,19 @@ jobs:
|
|||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:qemu-v7.0.0-28
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
|
|
@ -17,8 +17,7 @@ on:
|
|||
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
# Temporary disable contaienrd main branch test and migrate to containerd v2 after it's released.
|
||||
# DOCKER_BUILD_ARGS: --build-arg=CONTAINERD_VERSION=main # do tests with the latest containerd
|
||||
DOCKER_BUILD_ARGS: --build-arg=CONTAINERD_VERSION=main # do tests with the latest containerd
|
||||
|
||||
jobs:
|
||||
integration:
|
||||
|
@ -93,7 +92,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23.x'
|
||||
go-version: '1.24.x'
|
||||
- name: Install k3d
|
||||
run: |
|
||||
wget -q -O - https://raw.githubusercontent.com/rancher/k3d/v5.6.3/install.sh | bash
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -38,11 +38,11 @@ jobs:
|
|||
fetch-depth: '0'
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23.x'
|
||||
go-version: '1.24.x'
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6.1.1
|
||||
uses: golangci/golangci-lint-action@v8.0.0
|
||||
with:
|
||||
version: v1.61.0
|
||||
version: v2.1
|
||||
args: --verbose --timeout=10m
|
||||
working-directory: ${{ matrix.targetdir }}
|
||||
|
||||
|
@ -52,18 +52,37 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Temporary disable containerd main branch test and migrate to containerd v2 after it's released.
|
||||
# buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
buildargs: [""]
|
||||
buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
builtin: ["true", "false"]
|
||||
metadata-store: ["memory", "db"]
|
||||
fuse-passthrough: ["true", "false"]
|
||||
fuse-manager: ["true", "false"]
|
||||
transfer-service: ["true", "false"]
|
||||
exclude:
|
||||
# - buildargs: ""
|
||||
# builtin: "true"
|
||||
- buildargs: ""
|
||||
builtin: "true"
|
||||
- metadata-store: "db"
|
||||
builtin: "true"
|
||||
# - metadata-store: "db"
|
||||
# buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- metadata-store: "db"
|
||||
buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- fuse-passthrough: "true"
|
||||
builtin: "true"
|
||||
- fuse-passthrough: "true"
|
||||
buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- fuse-passthrough: "true"
|
||||
metadata-store: "db"
|
||||
- fuse-manager: "true"
|
||||
builtin: "true"
|
||||
- fuse-manager: "true"
|
||||
buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- transfer-service: "true"
|
||||
buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- transfer-service: "true"
|
||||
builtin: "true"
|
||||
- transfer-service: "true"
|
||||
metadata-store: "db"
|
||||
- transfer-service: "true"
|
||||
fuse-passthrough: "true"
|
||||
steps:
|
||||
- name: Install htpasswd for setting up private registry
|
||||
run: sudo apt-get update -y && sudo apt-get --no-install-recommends install -y apache2-utils
|
||||
|
@ -73,6 +92,9 @@ jobs:
|
|||
DOCKER_BUILD_ARGS: ${{ matrix.buildargs }}
|
||||
BUILTIN_SNAPSHOTTER: ${{ matrix.builtin }}
|
||||
METADATA_STORE: ${{ matrix.metadata-store }}
|
||||
FUSE_PASSTHROUGH: ${{ matrix.fuse-passthrough }}
|
||||
FUSE_MANAGER: ${{ matrix.fuse-manager }}
|
||||
TRANSFER_SERVICE: ${{ matrix.transfer-service }}
|
||||
run: make integration
|
||||
|
||||
test-optimize:
|
||||
|
@ -81,9 +103,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Temporary disable contaienrd main branch test and migrate to containerd v2 after it's released.
|
||||
# buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
buildargs: [""]
|
||||
buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
steps:
|
||||
- name: Install htpasswd for setting up private registry
|
||||
run: sudo apt-get update -y && sudo apt-get --no-install-recommends install -y apache2-utils
|
||||
|
@ -99,13 +119,11 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Temporary disable contaienrd main branch test and migrate to containerd v2 after it's released.
|
||||
# buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
buildargs: [""]
|
||||
buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
builtin: ["true", "false"]
|
||||
# exclude:
|
||||
# - buildargs: ""
|
||||
# builtin: "true"
|
||||
exclude:
|
||||
- buildargs: ""
|
||||
builtin: "true"
|
||||
steps:
|
||||
- name: Install htpasswd for setting up private registry
|
||||
run: sudo apt-get update -y && sudo apt-get --no-install-recommends install -y apache2-utils
|
||||
|
@ -122,13 +140,11 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Temporary disable contaienrd main branch test and migrate to containerd v2 after it's released.
|
||||
# buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
buildargs: [""]
|
||||
buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
builtin: ["true", "false"]
|
||||
# exclude:
|
||||
# - buildargs: ""
|
||||
# builtin: "true"
|
||||
exclude:
|
||||
- buildargs: ""
|
||||
builtin: "true"
|
||||
steps:
|
||||
- name: Install htpasswd for setting up private registry
|
||||
run: sudo apt-get update -y && sudo apt-get --no-install-recommends install -y apache2-utils
|
||||
|
@ -145,18 +161,37 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Temporary disable contaienrd main branch test and migrate to containerd v2 after it's released.
|
||||
# buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
buildargs: [""]
|
||||
buildargs: ["", "--build-arg=CONTAINERD_VERSION=main"] # released version & main version
|
||||
builtin: ["true", "false"]
|
||||
metadata-store: ["memory", "db"]
|
||||
fuse-passthrough: ["true", "false"]
|
||||
fuse-manager: ["true", "false"]
|
||||
transfer-service: ["true", "false"]
|
||||
exclude:
|
||||
# - buildargs: ""
|
||||
# builtin: "true"
|
||||
- buildargs: ""
|
||||
builtin: "true"
|
||||
- metadata-store: "db"
|
||||
builtin: "true"
|
||||
# - metadata-store: "db"
|
||||
# buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- metadata-store: "db"
|
||||
buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- fuse-passthrough: "true"
|
||||
builtin: "true"
|
||||
- fuse-passthrough: "true"
|
||||
buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- fuse-passthrough: "true"
|
||||
metadata-store: "db"
|
||||
- fuse-manager: "true"
|
||||
builtin: "true"
|
||||
- fuse-manager: "true"
|
||||
buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- transfer-service: "true"
|
||||
buildargs: "--build-arg=CONTAINERD_VERSION=main"
|
||||
- transfer-service: "true"
|
||||
builtin: "true"
|
||||
- transfer-service: "true"
|
||||
metadata-store: "db"
|
||||
- transfer-service: "true"
|
||||
fuse-passthrough: "true"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Validate containerd through CRI
|
||||
|
@ -164,6 +199,9 @@ jobs:
|
|||
DOCKER_BUILD_ARGS: ${{ matrix.buildargs }}
|
||||
BUILTIN_SNAPSHOTTER: ${{ matrix.builtin }}
|
||||
METADATA_STORE: ${{ matrix.metadata-store }}
|
||||
FUSE_PASSTHROUGH: ${{ matrix.fuse-passthrough }}
|
||||
FUSE_MANAGER: ${{ matrix.fuse-manager }}
|
||||
TRANSFER_SERVICE: ${{ matrix.transfer-service }}
|
||||
run: make test-cri-containerd
|
||||
|
||||
test-cri-cri-o:
|
||||
|
@ -203,7 +241,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23.x'
|
||||
go-version: '1.24.x'
|
||||
- name: Install k3d
|
||||
run: |
|
||||
wget -q -O - https://raw.githubusercontent.com/rancher/k3d/v5.6.3/install.sh | bash
|
||||
|
@ -233,7 +271,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23.x'
|
||||
go-version: '1.24.x'
|
||||
- name: Install k3d
|
||||
run: |
|
||||
wget -q -O - https://raw.githubusercontent.com/rancher/k3d/v5.6.3/install.sh | bash
|
||||
|
@ -278,14 +316,34 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23.x'
|
||||
go-version: '1.24.x'
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: src/github.com/containerd/stargz-snapshotter
|
||||
fetch-depth: 25
|
||||
- uses: containerd/project-checks@v1.1.0
|
||||
- uses: containerd/project-checks@v1.2.2
|
||||
with:
|
||||
working-directory: src/github.com/containerd/stargz-snapshotter
|
||||
# go-licenses-ignore is set because go-licenses cannot correctly detect the license of the following packages:
|
||||
# * estargz packages: Apache-2.0 and BSD-3-Clause dual license
|
||||
# (https://github.com/containerd/stargz-snapshotter/blob/main/NOTICE.md)
|
||||
#
|
||||
# The list of the CNCF-approved licenses can be found here:
|
||||
# https://github.com/cncf/foundation/blob/main/allowed-third-party-license-policy.md
|
||||
#
|
||||
# hashicorp packages: MPL-2.0
|
||||
# (https://github.com/hashicorp/go-cleanhttp/blob/master/LICENSE,
|
||||
# https://github.com/hashicorp/go-retryablehttp/blob/master/LICENSE)
|
||||
# Note: MPL-2.0 is not in the CNCF-approved licenses list, but these packages are allowed as exceptions.
|
||||
# See CNCF licensing exceptions:
|
||||
# https://github.com/cncf/foundation/blob/main/license-exceptions/CNCF-licensing-exceptions.csv
|
||||
go-licenses-ignore: |
|
||||
github.com/containerd/stargz-snapshotter/estargz
|
||||
github.com/containerd/stargz-snapshotter/estargz/errorutil
|
||||
github.com/containerd/stargz-snapshotter/estargz/externaltoc
|
||||
github.com/containerd/stargz-snapshotter/estargz/zstdchunked
|
||||
github.com/hashicorp/go-cleanhttp
|
||||
github.com/hashicorp/go-retryablehttp
|
||||
- name: Check proto generated code
|
||||
run: make validate-generated
|
||||
working-directory: src/github.com/containerd/stargz-snapshotter
|
||||
|
|
|
@ -1,41 +1,54 @@
|
|||
# This is applied to `estargz` submodule as well.
|
||||
# https://golangci-lint.run/usage/configuration#config-file
|
||||
|
||||
version: "2"
|
||||
linters:
|
||||
enable:
|
||||
- depguard # Checks for imports that shouldn't be used.
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- gofmt
|
||||
- goimports
|
||||
- revive
|
||||
- ineffassign
|
||||
- govet
|
||||
- unused
|
||||
- depguard
|
||||
- misspell
|
||||
- revive
|
||||
- unconvert
|
||||
disable:
|
||||
- errcheck
|
||||
|
||||
linters-settings:
|
||||
depguard:
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: github.com/containerd/containerd/errdefs
|
||||
desc: The containerd errdefs package was migrated to a separate module. Use github.com/containerd/errdefs instead.
|
||||
- pkg: github.com/containerd/containerd/log
|
||||
desc: The containerd log package was migrated to a separate module. Use github.com/containerd/log instead.
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: "github.com/containerd/containerd/errdefs"
|
||||
desc: The containerd errdefs package was migrated to a separate module. Use github.com/containerd/errdefs instead.
|
||||
- pkg: "github.com/containerd/containerd/log"
|
||||
desc: The containerd log package was migrated to a separate module. Use github.com/containerd/log instead.
|
||||
|
||||
run:
|
||||
deadline: 4m
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- linters:
|
||||
- revive
|
||||
text: "unused-parameter"
|
||||
exclude-dirs:
|
||||
- docs
|
||||
- images
|
||||
- out
|
||||
- script
|
||||
- linters:
|
||||
- revive
|
||||
text: unused-parameter
|
||||
- linters:
|
||||
- revive
|
||||
text: redefines-builtin-id
|
||||
paths:
|
||||
- docs
|
||||
- images
|
||||
- out
|
||||
- script
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- docs
|
||||
- images
|
||||
- out
|
||||
- script
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
|
66
Dockerfile
66
Dockerfile
|
@ -12,46 +12,45 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG CONTAINERD_VERSION=v2.0.0
|
||||
ARG RUNC_VERSION=v1.2.1
|
||||
ARG CNI_PLUGINS_VERSION=v1.6.0
|
||||
# TODO: support v2
|
||||
ARG NERDCTL_VERSION=1.7.7
|
||||
ARG CONTAINERD_VERSION=v2.1.3
|
||||
ARG RUNC_VERSION=v1.3.0
|
||||
ARG CNI_PLUGINS_VERSION=v1.7.1
|
||||
ARG NERDCTL_VERSION=2.1.3
|
||||
|
||||
ARG PODMAN_VERSION=v5.2.5
|
||||
ARG CRIO_VERSION=v1.31.2
|
||||
ARG CONMON_VERSION=v2.1.12
|
||||
ARG COMMON_VERSION=v0.61.0
|
||||
ARG PODMAN_VERSION=v5.5.2
|
||||
ARG CRIO_VERSION=v1.33.2
|
||||
ARG CONMON_VERSION=v2.1.13
|
||||
ARG COMMON_VERSION=v0.63.0
|
||||
ARG CRIO_TEST_PAUSE_IMAGE_NAME=registry.k8s.io/pause:3.6
|
||||
ARG NETAVARK_VERSION=v1.13.0
|
||||
ARG NETAVARK_VERSION=v1.15.2
|
||||
|
||||
ARG CONTAINERIZED_SYSTEMD_VERSION=v0.1.1
|
||||
ARG SLIRP4NETNS_VERSION=v1.3.1
|
||||
ARG PAUSE_IMAGE_NAME_TEST=registry.k8s.io/pause:3.10
|
||||
ARG SLIRP4NETNS_VERSION=v1.3.3
|
||||
ARG PAUSE_IMAGE_NAME_TEST=registry.k8s.io/pause:3.10.1
|
||||
|
||||
# Used in CI
|
||||
ARG CRI_TOOLS_VERSION=v1.30.0
|
||||
ARG CRI_TOOLS_VERSION=v1.30.1
|
||||
|
||||
# Legacy builder that doesn't support TARGETARCH should set this explicitly using --build-arg.
|
||||
# If TARGETARCH isn't supported by the builder, the default value is "amd64".
|
||||
|
||||
FROM golang:1.23-bullseye AS golang-base
|
||||
FROM golang:1.24-bullseye AS golang-base
|
||||
|
||||
# Build containerd
|
||||
FROM golang-base AS containerd-dev
|
||||
FROM --platform=$BUILDPLATFORM golang:1.24-bullseye AS containerd-dev
|
||||
ARG CONTAINERD_VERSION
|
||||
RUN apt-get update -y && apt-get install -y libbtrfs-dev libseccomp-dev && \
|
||||
git clone -b ${CONTAINERD_VERSION} --depth 1 \
|
||||
ARG TARGETARCH
|
||||
RUN git clone -b ${CONTAINERD_VERSION} --depth 1 \
|
||||
https://github.com/containerd/containerd $GOPATH/src/github.com/containerd/containerd && \
|
||||
cd $GOPATH/src/github.com/containerd/containerd && \
|
||||
make && DESTDIR=/out/ PREFIX= make install
|
||||
GOARCH=$TARGETARCH make && DESTDIR=/out/ PREFIX= make install
|
||||
|
||||
# Build containerd with builtin stargz snapshotter
|
||||
FROM golang-base AS containerd-snapshotter-dev
|
||||
FROM --platform=$BUILDPLATFORM golang:1.24-bullseye AS containerd-snapshotter-dev
|
||||
ARG CONTAINERD_VERSION
|
||||
ARG TARGETARCH
|
||||
COPY . $GOPATH/src/github.com/containerd/stargz-snapshotter
|
||||
RUN apt-get update -y && apt-get install -y libbtrfs-dev libseccomp-dev && \
|
||||
git clone -b ${CONTAINERD_VERSION} --depth 1 \
|
||||
RUN git clone -b ${CONTAINERD_VERSION} --depth 1 \
|
||||
https://github.com/containerd/containerd $GOPATH/src/github.com/containerd/containerd && \
|
||||
cd $GOPATH/src/github.com/containerd/containerd && \
|
||||
echo 'require github.com/containerd/stargz-snapshotter v0.0.0' >> go.mod && \
|
||||
|
@ -66,21 +65,11 @@ RUN apt-get update -y && apt-get install -y libbtrfs-dev libseccomp-dev && \
|
|||
echo 'replace github.com/containerd/stargz-snapshotter => '$GOPATH'/src/github.com/containerd/stargz-snapshotter' >> integration/client/go.mod && \
|
||||
echo 'replace github.com/containerd/stargz-snapshotter/estargz => '$GOPATH'/src/github.com/containerd/stargz-snapshotter/estargz' >> integration/client/go.mod ; \
|
||||
fi && \
|
||||
if [ "$(echo -n ${CONTAINERD_VERSION} | head -c 4)" = "v1.7" ] ; then \
|
||||
# containerd v1.7 doesn't support cri-api >= v0.28 which adds RuntimeConfig API
|
||||
echo 'replace k8s.io/cri-api => k8s.io/cri-api v0.27.1' >> go.mod ; \
|
||||
if [ -f api/go.mod ] ; then \
|
||||
echo 'replace k8s.io/cri-api => k8s.io/cri-api v0.27.1' >> api/go.mod ; \
|
||||
fi ; \
|
||||
if [ -f integration/client/go.mod ] ; then \
|
||||
echo 'replace k8s.io/cri-api => k8s.io/cri-api v0.27.1' >> integration/client/go.mod ; \
|
||||
fi ; \
|
||||
fi && \
|
||||
echo 'package main \nimport _ "github.com/containerd/stargz-snapshotter/service/plugin"' > cmd/containerd/builtins_stargz_snapshotter.go && \
|
||||
make vendor && make && DESTDIR=/out/ PREFIX= make install
|
||||
make vendor && GOARCH=$TARGETARCH make && DESTDIR=/out/ PREFIX= make install
|
||||
|
||||
# Build runc
|
||||
FROM golang:1.23-bullseye AS runc-dev
|
||||
FROM golang:1.24-bullseye AS runc-dev
|
||||
ARG RUNC_VERSION
|
||||
RUN apt-get update -y && apt-get install -y libseccomp-dev && \
|
||||
git clone -b ${RUNC_VERSION} --depth 1 \
|
||||
|
@ -89,7 +78,7 @@ RUN apt-get update -y && apt-get install -y libseccomp-dev && \
|
|||
make && make install PREFIX=/out/
|
||||
|
||||
# Build stargz snapshotter
|
||||
FROM golang-base AS snapshotter-dev
|
||||
FROM --platform=$BUILDPLATFORM golang:1.24-bullseye AS snapshotter-dev
|
||||
ARG TARGETARCH
|
||||
ARG GOARM
|
||||
ARG SNAPSHOTTER_BUILD_FLAGS
|
||||
|
@ -98,7 +87,8 @@ COPY . $GOPATH/src/github.com/containerd/stargz-snapshotter
|
|||
ARG CGO_ENABLED
|
||||
RUN cd $GOPATH/src/github.com/containerd/stargz-snapshotter && \
|
||||
PREFIX=/out/ GOARCH=${TARGETARCH:-amd64} GO_BUILD_FLAGS=${SNAPSHOTTER_BUILD_FLAGS} make containerd-stargz-grpc && \
|
||||
PREFIX=/out/ GOARCH=${TARGETARCH:-amd64} GO_BUILD_FLAGS=${CTR_REMOTE_BUILD_FLAGS} make ctr-remote
|
||||
PREFIX=/out/ GOARCH=${TARGETARCH:-amd64} GO_BUILD_FLAGS=${CTR_REMOTE_BUILD_FLAGS} make ctr-remote && \
|
||||
PREFIX=/out/ GOARCH=${TARGETARCH:-amd64} GO_BUILD_FLAGS=${CTR_REMOTE_BUILD_FLAGS} make stargz-fuse-manager
|
||||
|
||||
# Build stargz store
|
||||
FROM golang-base AS stargz-store-dev
|
||||
|
@ -122,7 +112,7 @@ RUN apt-get update -y && apt-get install -y libseccomp-dev libgpgme-dev && \
|
|||
|
||||
# Build CRI-O
|
||||
# FROM golang-base AS cri-o-dev
|
||||
FROM golang:1.23-bullseye AS cri-o-dev
|
||||
FROM golang:1.24-bullseye AS cri-o-dev
|
||||
ARG CRIO_VERSION
|
||||
RUN apt-get update -y && apt-get install -y libseccomp-dev libgpgme-dev && \
|
||||
git clone https://github.com/cri-o/cri-o $GOPATH/src/github.com/cri-o/cri-o && \
|
||||
|
@ -256,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.30.0 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/ /
|
||||
|
@ -297,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.30.0
|
||||
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/ /
|
||||
|
|
5
Makefile
5
Makefile
|
@ -24,7 +24,7 @@ REVISION=$(shell git rev-parse HEAD)$(shell if ! git diff --no-ext-diff --quiet
|
|||
GO_BUILD_LDFLAGS ?= -s -w
|
||||
GO_LD_FLAGS=-ldflags '$(GO_BUILD_LDFLAGS) -X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) $(GO_EXTRA_LDFLAGS)'
|
||||
|
||||
CMD=containerd-stargz-grpc ctr-remote stargz-store
|
||||
CMD=containerd-stargz-grpc ctr-remote stargz-store stargz-fuse-manager
|
||||
|
||||
CMD_BINARIES=$(addprefix $(PREFIX),$(CMD))
|
||||
|
||||
|
@ -48,6 +48,9 @@ stargz-store: FORCE
|
|||
stargz-store-helper: FORCE
|
||||
cd cmd/ ; GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./stargz-store/helper
|
||||
|
||||
stargz-fuse-manager: FORCE
|
||||
cd cmd/ ; GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./stargz-fuse-manager
|
||||
|
||||
check:
|
||||
@echo "$@"
|
||||
@GO111MODULE=$(GO111MODULE_VALUE) $(shell go env GOPATH)/bin/golangci-lint run
|
||||
|
|
|
@ -62,6 +62,8 @@ version = 2
|
|||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
|
||||
# Use stargz snapshotter through CRI
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
|
@ -69,8 +71,6 @@ version = 2
|
|||
disable_snapshot_annotations = false
|
||||
```
|
||||
|
||||
**Note that `disable_snapshot_annotations = false` is required since containerd > v1.4.2**
|
||||
|
||||
You can try our [prebuilt](/Dockerfile) [KinD](https://github.com/kubernetes-sigs/kind) node image that contains the above configuration.
|
||||
|
||||
```console
|
||||
|
|
|
@ -225,6 +225,9 @@ func Analyze(ctx context.Context, client *containerd.Client, ref string, opts ..
|
|||
successCount++
|
||||
}
|
||||
}()
|
||||
if err := task.Start(ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if aOpts.terminal {
|
||||
if err := tasks.HandleConsoleResize(ctx, task, con); err != nil {
|
||||
log.G(ctx).WithError(err).Error("failed to resize console")
|
||||
|
@ -233,9 +236,6 @@ func Analyze(ctx context.Context, client *containerd.Client, ref string, opts ..
|
|||
sigc := commands.ForwardAllSignals(ctx, task)
|
||||
defer commands.StopCatch(sigc)
|
||||
}
|
||||
if err := task.Start(ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Wait until the task exit
|
||||
var status containerd.ExitStatus
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package fanotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"sync"
|
||||
|
@ -24,7 +25,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/containerd/stargz-snapshotter/analyzer/fanotify/conn"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
// Fanotifier monitors "/" mountpoint of a new mount namespace and notifies all
|
||||
|
@ -59,14 +59,15 @@ func SpawnFanotifier(fanotifierBin string) (*Fanotifier, error) {
|
|||
|
||||
// Connect to the spawned fanotifier over stdio
|
||||
conn: conn.NewClient(notifyR, notifyW, cmd.Process.Pid, 5*time.Second),
|
||||
closeFunc: func() (allErr error) {
|
||||
closeFunc: func() error {
|
||||
var errs []error
|
||||
if err := notifyR.Close(); err != nil {
|
||||
allErr = multierror.Append(allErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := notifyW.Close(); err != nil {
|
||||
allErr = multierror.Append(allErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return
|
||||
return errors.Join(errs...)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ func Serve(target string, r io.Reader, w io.Writer) error {
|
|||
return fmt.Errorf("read fanotify fd: %w", err)
|
||||
}
|
||||
if event.Vers != unix.FANOTIFY_METADATA_VERSION {
|
||||
return fmt.Errorf("Fanotify version mismatch %d(got) != %d(want)",
|
||||
return fmt.Errorf("fanotify version mismatch %d(got) != %d(want)",
|
||||
event.Vers, unix.FANOTIFY_METADATA_VERSION)
|
||||
}
|
||||
if event.Fd < 0 {
|
||||
|
|
|
@ -151,7 +151,7 @@ func (r *ImageRecorder) Record(name string) error {
|
|||
}
|
||||
whDir := cleanEntryName(path.Join(path.Dir("/"+name), whiteoutOpaqueDir))
|
||||
if _, ok := r.index[i][whDir]; ok {
|
||||
return fmt.Errorf("Parent dir of %q is a deleted directory", name)
|
||||
return fmt.Errorf("parent dir of %q is a deleted directory", name)
|
||||
}
|
||||
}
|
||||
if index < 0 {
|
||||
|
|
|
@ -18,6 +18,7 @@ package cache
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -26,7 +27,7 @@ import (
|
|||
|
||||
"github.com/containerd/stargz-snapshotter/util/cacheutil"
|
||||
"github.com/containerd/stargz-snapshotter/util/namedmutex"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"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.
|
||||
|
@ -82,6 +86,9 @@ type BlobCache interface {
|
|||
type Reader interface {
|
||||
io.ReaderAt
|
||||
Close() error
|
||||
|
||||
// If a blob is backed by a file, it should return *os.File so that it can be used for FUSE passthrough
|
||||
GetReaderAt() io.ReaderAt
|
||||
}
|
||||
|
||||
// Writer enables the client to cache byte data. Commit() must be
|
||||
|
@ -94,7 +101,8 @@ type Writer interface {
|
|||
}
|
||||
|
||||
type cacheOpt struct {
|
||||
direct bool
|
||||
direct bool
|
||||
passThrough bool
|
||||
}
|
||||
|
||||
type Option func(o *cacheOpt) *cacheOpt
|
||||
|
@ -110,6 +118,15 @@ func Direct() Option {
|
|||
}
|
||||
}
|
||||
|
||||
// PassThrough option indicates whether to enable FUSE passthrough mode
|
||||
// to improve local file read performance.
|
||||
func PassThrough() Option {
|
||||
return func(o *cacheOpt) *cacheOpt {
|
||||
o.passThrough = true
|
||||
return o
|
||||
}
|
||||
}
|
||||
|
||||
func NewDirectoryCache(directory string, config DirectoryCacheConfig) (BlobCache, error) {
|
||||
if !filepath.IsAbs(directory) {
|
||||
return nil, fmt.Errorf("dir cache path must be an absolute path; got %q", directory)
|
||||
|
@ -160,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
|
||||
|
@ -175,8 +193,9 @@ type directoryCache struct {
|
|||
|
||||
bufPool *sync.Pool
|
||||
|
||||
syncAdd bool
|
||||
direct bool
|
||||
syncAdd bool
|
||||
direct bool
|
||||
fadvDontNeed bool
|
||||
|
||||
closed bool
|
||||
closedMu sync.Mutex
|
||||
|
@ -229,8 +248,22 @@ func (dc *directoryCache) Get(key string, opts ...Option) (Reader, error) {
|
|||
// that won't be accessed immediately.
|
||||
if dc.direct || opt.direct {
|
||||
return &reader{
|
||||
ReaderAt: file,
|
||||
closeFunc: func() error { return file.Close() },
|
||||
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
|
||||
if opt.passThrough {
|
||||
return nil
|
||||
}
|
||||
return file.Close()
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -273,13 +306,20 @@ func (dc *directoryCache) Add(key string, opts ...Option) (Writer, error) {
|
|||
// Commit the cache contents
|
||||
c := dc.cachePath(key)
|
||||
if err := os.MkdirAll(filepath.Dir(c), os.ModePerm); err != nil {
|
||||
var allErr error
|
||||
var errs []error
|
||||
if err := os.Remove(wip.Name()); err != nil {
|
||||
allErr = multierror.Append(allErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return multierror.Append(allErr,
|
||||
fmt.Errorf("failed to create cache directory %q: %w", c, err))
|
||||
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 {
|
||||
|
@ -384,7 +424,7 @@ func (mc *MemoryCache) Get(key string, opts ...Option) (Reader, error) {
|
|||
defer mc.mu.Unlock()
|
||||
b, ok := mc.Membuf[key]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Missed cache: %q", key)
|
||||
return nil, fmt.Errorf("missed cache: %q", key)
|
||||
}
|
||||
return &reader{bytes.NewReader(b.Bytes()), func() error { return nil }}, nil
|
||||
}
|
||||
|
@ -414,6 +454,10 @@ type reader struct {
|
|||
|
||||
func (r *reader) Close() error { return r.closeFunc() }
|
||||
|
||||
func (r *reader) GetReaderAt() io.ReaderAt {
|
||||
return r.ReaderAt
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
io.WriteCloser
|
||||
commitFunc func() error
|
||||
|
@ -438,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
|
||||
}
|
||||
|
|
|
@ -35,10 +35,10 @@ import (
|
|||
"github.com/containerd/stargz-snapshotter/estargz"
|
||||
"github.com/containerd/stargz-snapshotter/metadata"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/rs/xid"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
errbolt "go.etcd.io/bbolt/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
|
@ -99,7 +99,7 @@ func NewReader(db *bolt.DB, sr *io.SectionReader, opts ...metadata.Option) (meta
|
|||
rOpts.Telemetry.GetFooterLatency(start)
|
||||
}
|
||||
|
||||
var allErr error
|
||||
var errs []error
|
||||
var tocR io.ReadCloser
|
||||
var decompressor metadata.Decompressor
|
||||
for _, d := range decompressors {
|
||||
|
@ -108,7 +108,7 @@ func NewReader(db *bolt.DB, sr *io.SectionReader, opts ...metadata.Option) (meta
|
|||
maybeTocBytes := footer[:fOffset]
|
||||
_, tocOffset, tocSize, err := d.ParseFooter(footer[fOffset:])
|
||||
if err != nil {
|
||||
allErr = multierror.Append(allErr, err)
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
if tocOffset >= 0 && tocSize <= 0 {
|
||||
|
@ -119,12 +119,14 @@ func NewReader(db *bolt.DB, sr *io.SectionReader, opts ...metadata.Option) (meta
|
|||
}
|
||||
tocR, err = decompressTOC(d, sr, tocOffset, tocSize, maybeTocBytes, rOpts)
|
||||
if err != nil {
|
||||
allErr = multierror.Append(allErr, err)
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
decompressor = d
|
||||
break
|
||||
}
|
||||
|
||||
allErr := errors.Join(errs...)
|
||||
if tocR == nil {
|
||||
if allErr == nil {
|
||||
return nil, fmt.Errorf("failed to get the reader of TOC: unknown")
|
||||
|
@ -222,7 +224,7 @@ func (r *reader) init(decompressedR io.Reader, rOpts metadata.Options) (retErr e
|
|||
for i := 0; i < 100; i++ {
|
||||
fsID := xid.New().String()
|
||||
if err := r.initRootNode(fsID); err != nil {
|
||||
if errors.Is(err, bolt.ErrBucketExists) {
|
||||
if errors.Is(err, errbolt.ErrBucketExists) {
|
||||
continue // try with another id
|
||||
}
|
||||
return fmt.Errorf("failed to initialize root node %q: %w", fsID, err)
|
||||
|
@ -238,20 +240,22 @@ func (r *reader) init(decompressedR io.Reader, rOpts metadata.Options) (retErr e
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
closeFunc := func() (closeErr error) {
|
||||
closeFunc := func() error {
|
||||
name := f.Name()
|
||||
var errs []error
|
||||
if err := f.Close(); err != nil {
|
||||
closeErr = multierror.Append(closeErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := os.Remove(name); err != nil {
|
||||
closeErr = multierror.Append(closeErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
if err := closeFunc(); err != nil {
|
||||
retErr = multierror.Append(retErr, err)
|
||||
retErr = errors.Join(retErr, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fsopts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containerd/log"
|
||||
dbmetadata "github.com/containerd/stargz-snapshotter/cmd/containerd-stargz-grpc/db"
|
||||
ipfs "github.com/containerd/stargz-snapshotter/cmd/containerd-stargz-grpc/ipfs"
|
||||
"github.com/containerd/stargz-snapshotter/fs"
|
||||
"github.com/containerd/stargz-snapshotter/metadata"
|
||||
memorymetadata "github.com/containerd/stargz-snapshotter/metadata/memory"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
EnableIpfs bool
|
||||
MetadataStore string
|
||||
OpenBoltDB func(string) (*bolt.DB, error)
|
||||
}
|
||||
|
||||
const (
|
||||
memoryMetadataType = "memory"
|
||||
dbMetadataType = "db"
|
||||
)
|
||||
|
||||
func ConfigFsOpts(ctx context.Context, rootDir string, config *Config) ([]fs.Option, error) {
|
||||
fsOpts := []fs.Option{fs.WithMetricsLogLevel(log.InfoLevel)}
|
||||
|
||||
if config.EnableIpfs {
|
||||
fsOpts = append(fsOpts, fs.WithResolveHandler("ipfs", new(ipfs.ResolveHandler)))
|
||||
}
|
||||
|
||||
mt, err := getMetadataStore(rootDir, config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to configure metadata store: %w", err)
|
||||
}
|
||||
fsOpts = append(fsOpts, fs.WithMetadataStore(mt))
|
||||
|
||||
return fsOpts, nil
|
||||
}
|
||||
|
||||
func getMetadataStore(rootDir string, config *Config) (metadata.Store, error) {
|
||||
switch config.MetadataStore {
|
||||
case "", memoryMetadataType:
|
||||
return memorymetadata.NewReader, nil
|
||||
case dbMetadataType:
|
||||
if config.OpenBoltDB == nil {
|
||||
return nil, fmt.Errorf("bolt DB is not configured")
|
||||
}
|
||||
db, err := config.OpenBoltDB(filepath.Join(rootDir, "metadata.db"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(sr *io.SectionReader, opts ...metadata.Option) (metadata.Reader, error) {
|
||||
return dbmetadata.NewReader(db, sr, opts...)
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown metadata store type: %v; must be %v or %v",
|
||||
config.MetadataStore, memoryMetadataType, dbMetadataType)
|
||||
}
|
||||
}
|
|
@ -20,12 +20,12 @@ import (
|
|||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
golog "log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
@ -33,20 +33,13 @@ import (
|
|||
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
|
||||
"github.com/containerd/containerd/v2/contrib/snapshotservice"
|
||||
"github.com/containerd/containerd/v2/core/snapshots"
|
||||
"github.com/containerd/containerd/v2/defaults"
|
||||
"github.com/containerd/containerd/v2/pkg/dialer"
|
||||
"github.com/containerd/containerd/v2/pkg/sys"
|
||||
"github.com/containerd/log"
|
||||
dbmetadata "github.com/containerd/stargz-snapshotter/cmd/containerd-stargz-grpc/db"
|
||||
ipfs "github.com/containerd/stargz-snapshotter/cmd/containerd-stargz-grpc/ipfs"
|
||||
"github.com/containerd/stargz-snapshotter/fs"
|
||||
"github.com/containerd/stargz-snapshotter/metadata"
|
||||
memorymetadata "github.com/containerd/stargz-snapshotter/metadata/memory"
|
||||
"github.com/containerd/stargz-snapshotter/cmd/containerd-stargz-grpc/fsopts"
|
||||
"github.com/containerd/stargz-snapshotter/fusemanager"
|
||||
"github.com/containerd/stargz-snapshotter/service"
|
||||
"github.com/containerd/stargz-snapshotter/service/keychain/cri"
|
||||
"github.com/containerd/stargz-snapshotter/service/keychain/dockerconfig"
|
||||
"github.com/containerd/stargz-snapshotter/service/keychain/kubeconfig"
|
||||
"github.com/containerd/stargz-snapshotter/service/resolver"
|
||||
"github.com/containerd/stargz-snapshotter/service/keychain/keychainconfig"
|
||||
snbase "github.com/containerd/stargz-snapshotter/snapshot"
|
||||
"github.com/containerd/stargz-snapshotter/version"
|
||||
sddaemon "github.com/coreos/go-systemd/v22/daemon"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
|
@ -54,9 +47,6 @@ import (
|
|||
bolt "go.etcd.io/bbolt"
|
||||
"golang.org/x/sys/unix"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -65,6 +55,8 @@ const (
|
|||
defaultLogLevel = log.InfoLevel
|
||||
defaultRootDir = "/var/lib/containerd-stargz-grpc"
|
||||
defaultImageServiceAddress = "/run/containerd/containerd.sock"
|
||||
defaultFuseManagerAddress = "/run/containerd-stargz-grpc/fuse-manager.sock"
|
||||
fuseManagerBin = "stargz-fuse-manager"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -79,19 +71,33 @@ type snapshotterConfig struct {
|
|||
service.Config
|
||||
|
||||
// MetricsAddress is address for the metrics API
|
||||
MetricsAddress string `toml:"metrics_address"`
|
||||
MetricsAddress string `toml:"metrics_address" json:"metrics_address"`
|
||||
|
||||
// NoPrometheus is a flag to disable the emission of the metrics
|
||||
NoPrometheus bool `toml:"no_prometheus"`
|
||||
NoPrometheus bool `toml:"no_prometheus" json:"no_prometheus"`
|
||||
|
||||
// DebugAddress is a Unix domain socket address where the snapshotter exposes /debug/ endpoints.
|
||||
DebugAddress string `toml:"debug_address"`
|
||||
DebugAddress string `toml:"debug_address" json:"debug_address"`
|
||||
|
||||
// IPFS is a flag to enbale lazy pulling from IPFS.
|
||||
IPFS bool `toml:"ipfs"`
|
||||
IPFS bool `toml:"ipfs" json:"ipfs"`
|
||||
|
||||
// MetadataStore is the type of the metadata store to use.
|
||||
MetadataStore string `toml:"metadata_store" default:"memory"`
|
||||
MetadataStore string `toml:"metadata_store" default:"memory" json:"metadata_store"`
|
||||
|
||||
// FuseManagerConfig is configuration for fusemanager
|
||||
FuseManagerConfig `toml:"fuse_manager" json:"fuse_manager"`
|
||||
}
|
||||
|
||||
type FuseManagerConfig struct {
|
||||
// Enable is whether detach fusemanager or not
|
||||
Enable bool `toml:"enable" default:"false" json:"enable"`
|
||||
|
||||
// Address is address for the fusemanager's GRPC server (default: "/run/containerd-stargz-grpc/fuse-manager.sock")
|
||||
Address string `toml:"address" json:"address"`
|
||||
|
||||
// Path is path to the fusemanager's executable (default: looking for a binary "stargz-fuse-manager")
|
||||
Path string `toml:"path" json:"path"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -118,7 +124,7 @@ func main() {
|
|||
|
||||
// Get configuration from specified file
|
||||
tree, err := toml.LoadFile(*configPath)
|
||||
if err != nil && !(os.IsNotExist(err) && *configPath == defaultConfigPath) {
|
||||
if err != nil && (!os.IsNotExist(err) || *configPath != defaultConfigPath) {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to load config file %q", *configPath)
|
||||
}
|
||||
if err := tree.Unmarshal(&config); err != nil {
|
||||
|
@ -132,45 +138,126 @@ func main() {
|
|||
// Create a gRPC server
|
||||
rpc := grpc.NewServer()
|
||||
|
||||
// Configure FUSE passthrough
|
||||
// Always set Direct to true to ensure that
|
||||
// *directoryCache.Get always return *os.File instead of buffer
|
||||
if config.PassThrough {
|
||||
config.Direct = true
|
||||
}
|
||||
|
||||
// Configure keychain
|
||||
credsFuncs := []resolver.Credential{dockerconfig.NewDockerconfigKeychain(ctx)}
|
||||
if config.Config.KubeconfigKeychainConfig.EnableKeychain {
|
||||
var opts []kubeconfig.Option
|
||||
if kcp := config.Config.KubeconfigKeychainConfig.KubeconfigPath; kcp != "" {
|
||||
opts = append(opts, kubeconfig.WithKubeconfigPath(kcp))
|
||||
}
|
||||
credsFuncs = append(credsFuncs, kubeconfig.NewKubeconfigKeychain(ctx, opts...))
|
||||
keyChainConfig := keychainconfig.Config{
|
||||
EnableKubeKeychain: config.KubeconfigKeychainConfig.EnableKeychain,
|
||||
EnableCRIKeychain: config.CRIKeychainConfig.EnableKeychain,
|
||||
KubeconfigPath: config.KubeconfigPath,
|
||||
DefaultImageServiceAddress: defaultImageServiceAddress,
|
||||
ImageServicePath: config.ImageServicePath,
|
||||
}
|
||||
if config.Config.CRIKeychainConfig.EnableKeychain {
|
||||
// connects to the backend CRI service (defaults to containerd socket)
|
||||
criAddr := defaultImageServiceAddress
|
||||
if cp := config.CRIKeychainConfig.ImageServicePath; cp != "" {
|
||||
criAddr = cp
|
||||
}
|
||||
connectCRI := func() (runtime.ImageServiceClient, error) {
|
||||
conn, err := newCRIConn(criAddr)
|
||||
|
||||
var rs snapshots.Snapshotter
|
||||
fuseManagerConfig := config.FuseManagerConfig
|
||||
if fuseManagerConfig.Enable {
|
||||
fmPath := fuseManagerConfig.Path
|
||||
if fmPath == "" {
|
||||
var err error
|
||||
fmPath, err = exec.LookPath(fuseManagerBin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.G(ctx).WithError(err).Fatalf("failed to find fusemanager bin")
|
||||
}
|
||||
return runtime.NewImageServiceClient(conn), nil
|
||||
}
|
||||
f, criServer := cri.NewCRIKeychain(ctx, connectCRI)
|
||||
runtime.RegisterImageServiceServer(rpc, criServer)
|
||||
credsFuncs = append(credsFuncs, f)
|
||||
}
|
||||
fsOpts := []fs.Option{fs.WithMetricsLogLevel(log.InfoLevel)}
|
||||
if config.IPFS {
|
||||
fsOpts = append(fsOpts, fs.WithResolveHandler("ipfs", new(ipfs.ResolveHandler)))
|
||||
}
|
||||
mt, err := getMetadataStore(*rootDir, config)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to configure metadata store")
|
||||
}
|
||||
fsOpts = append(fsOpts, fs.WithMetadataStore(mt))
|
||||
rs, err := service.NewStargzSnapshotterService(ctx, *rootDir, &config.Config,
|
||||
service.WithCredsFuncs(credsFuncs...), service.WithFilesystemOptions(fsOpts...))
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to configure snapshotter")
|
||||
fmAddr := fuseManagerConfig.Address
|
||||
if fmAddr == "" {
|
||||
fmAddr = defaultFuseManagerAddress
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(fmAddr) {
|
||||
log.G(ctx).WithError(err).Fatalf("fuse manager address must be an absolute path: %s", fmAddr)
|
||||
}
|
||||
managerNewlyStarted, err := fusemanager.StartFuseManager(ctx, fmPath, fmAddr, filepath.Join(*rootDir, "fusestore.db"), *logLevel, filepath.Join(*rootDir, "stargz-fuse-manager.log"))
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to start fusemanager")
|
||||
}
|
||||
|
||||
fuseManagerConfig := fusemanager.Config{
|
||||
Config: config.Config,
|
||||
IPFS: config.IPFS,
|
||||
MetadataStore: config.MetadataStore,
|
||||
DefaultImageServiceAddress: defaultImageServiceAddress,
|
||||
}
|
||||
|
||||
fs, err := fusemanager.NewManagerClient(ctx, *rootDir, fmAddr, &fuseManagerConfig)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to configure fusemanager")
|
||||
}
|
||||
flags := []snbase.Opt{snbase.AsynchronousRemove}
|
||||
// "managerNewlyStarted" being true indicates that the FUSE manager is newly started. To
|
||||
// fully recover the snapshotter and the FUSE manager's state, we need to restore
|
||||
// all snapshot mounts. If managerNewlyStarted is false, the existing FUSE manager maintains
|
||||
// snapshot mounts so we don't need to restore them.
|
||||
if !managerNewlyStarted {
|
||||
flags = append(flags, snbase.NoRestore)
|
||||
}
|
||||
rs, err = snbase.NewSnapshotter(ctx, filepath.Join(*rootDir, "snapshotter"), fs, flags...)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to configure snapshotter")
|
||||
}
|
||||
log.G(ctx).Infof("Start snapshotter with fusemanager mode")
|
||||
} else {
|
||||
crirpc := rpc
|
||||
// For CRI keychain, if listening path is different from stargz-snapshotter's socket, prepare for the dedicated grpc server and the socket.
|
||||
serveCRISocket := config.CRIKeychainConfig.EnableKeychain && config.ListenPath != "" && config.ListenPath != *address
|
||||
if serveCRISocket {
|
||||
crirpc = grpc.NewServer()
|
||||
}
|
||||
credsFuncs, err := keychainconfig.ConfigKeychain(ctx, crirpc, &keyChainConfig)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to configure keychain")
|
||||
}
|
||||
if serveCRISocket {
|
||||
addr := config.ListenPath
|
||||
// Prepare the directory for the socket
|
||||
if err := os.MkdirAll(filepath.Dir(addr), 0700); err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to create directory %q", filepath.Dir(addr))
|
||||
}
|
||||
|
||||
// Try to remove the socket file to avoid EADDRINUSE
|
||||
if err := os.RemoveAll(addr); err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to remove %q", addr)
|
||||
}
|
||||
|
||||
// Listen and serve
|
||||
l, err := net.Listen("unix", addr)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("error on listen socket %q", addr)
|
||||
}
|
||||
go func() {
|
||||
if err := crirpc.Serve(l); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("error on serving CRI via socket %q", addr)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
fsConfig := fsopts.Config{
|
||||
EnableIpfs: config.IPFS,
|
||||
MetadataStore: config.MetadataStore,
|
||||
OpenBoltDB: func(p string) (*bolt.DB, error) {
|
||||
return bolt.Open(p, 0600, &bolt.Options{
|
||||
NoFreelistSync: true,
|
||||
InitialMmapSize: 64 * 1024 * 1024,
|
||||
FreelistType: bolt.FreelistMapType,
|
||||
})
|
||||
},
|
||||
}
|
||||
fsOpts, err := fsopts.ConfigFsOpts(ctx, *rootDir, &fsConfig)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to configure fs config")
|
||||
}
|
||||
|
||||
rs, err = service.NewStargzSnapshotterService(ctx, *rootDir, &config.Config,
|
||||
service.WithCredsFuncs(credsFuncs...), service.WithFilesystemOptions(fsOpts...))
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to configure snapshotter")
|
||||
}
|
||||
}
|
||||
|
||||
cleanup, err := serve(ctx, rpc, *address, rs, config)
|
||||
|
@ -178,7 +265,18 @@ func main() {
|
|||
log.G(ctx).WithError(err).Fatalf("failed to serve snapshotter")
|
||||
}
|
||||
|
||||
if cleanup {
|
||||
// When FUSE manager is disabled, FUSE servers are goroutines in the
|
||||
// contaienrd-stargz-grpc process. So killing containerd-stargz-grpc will
|
||||
// result in all FUSE mount becoming unavailable with leaving all resources
|
||||
// (e.g. temporary cache) on the node. To ensure graceful shutdown, we
|
||||
// should always cleanup mounts and associated resources here.
|
||||
//
|
||||
// When FUSE manager is enabled, those mounts are still under the control by
|
||||
// the FUSE manager so we need to avoid cleaning them up unless explicitly
|
||||
// commanded via SIGINT. The user can use SIGINT to gracefully killing the FUSE
|
||||
// manager before rebooting the node for ensuring that the all snapshots are
|
||||
// unmounted with cleaning up associated temporary resources.
|
||||
if cleanup || !fuseManagerConfig.Enable {
|
||||
log.G(ctx).Debug("Closing the snapshotter")
|
||||
rs.Close()
|
||||
}
|
||||
|
@ -268,48 +366,3 @@ func serve(ctx context.Context, rpc *grpc.Server, addr string, rs snapshots.Snap
|
|||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
const (
|
||||
memoryMetadataType = "memory"
|
||||
dbMetadataType = "db"
|
||||
)
|
||||
|
||||
func getMetadataStore(rootDir string, config snapshotterConfig) (metadata.Store, error) {
|
||||
switch config.MetadataStore {
|
||||
case "", memoryMetadataType:
|
||||
return memorymetadata.NewReader, nil
|
||||
case dbMetadataType:
|
||||
bOpts := bolt.Options{
|
||||
NoFreelistSync: true,
|
||||
InitialMmapSize: 64 * 1024 * 1024,
|
||||
FreelistType: bolt.FreelistMapType,
|
||||
}
|
||||
db, err := bolt.Open(filepath.Join(rootDir, "metadata.db"), 0600, &bOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(sr *io.SectionReader, opts ...metadata.Option) (metadata.Reader, error) {
|
||||
return dbmetadata.NewReader(db, sr, opts...)
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown metadata store type: %v; must be %v or %v",
|
||||
config.MetadataStore, memoryMetadataType, dbMetadataType)
|
||||
}
|
||||
}
|
||||
|
||||
func newCRIConn(criAddr string) (*grpc.ClientConn, error) {
|
||||
// TODO: make gRPC options configurable from config.toml
|
||||
backoffConfig := backoff.DefaultConfig
|
||||
backoffConfig.MaxDelay = 3 * time.Second
|
||||
connParams := grpc.ConnectParams{
|
||||
Backoff: backoffConfig,
|
||||
}
|
||||
gopts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithConnectParams(connParams),
|
||||
grpc.WithContextDialer(dialer.ContextDialer),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
|
||||
}
|
||||
return grpc.NewClient(dialer.DialAddress(criAddr), gopts...)
|
||||
}
|
||||
|
|
|
@ -21,10 +21,12 @@ import (
|
|||
gocontext "context"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
containerd "github.com/containerd/containerd/v2/client"
|
||||
|
@ -36,7 +38,6 @@ import (
|
|||
"github.com/containerd/containerd/v2/pkg/oci"
|
||||
gocni "github.com/containerd/go-cni"
|
||||
"github.com/containerd/log"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/rs/xid"
|
||||
|
@ -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",
|
||||
|
@ -129,13 +148,14 @@ var samplerFlags = []cli.Flag{
|
|||
func getSpecOpts(clicontext *cli.Context) func(image containerd.Image, rootfs string) (opts []oci.SpecOpts, done func() error, rErr error) {
|
||||
return func(image containerd.Image, rootfs string) (opts []oci.SpecOpts, done func() error, rErr error) {
|
||||
var cleanups []func() error
|
||||
done = func() (allErr error) {
|
||||
done = func() error {
|
||||
var errs []error
|
||||
for i := len(cleanups) - 1; i >= 0; i-- {
|
||||
if err := cleanups[i](); err != nil {
|
||||
allErr = multierror.Append(allErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
defer func() {
|
||||
if rErr != nil {
|
||||
|
@ -212,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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,13 +290,14 @@ func withEntrypointArgs(clicontext *cli.Context, image containerd.Image) (oci.Sp
|
|||
|
||||
func withCNI(clicontext *cli.Context) (specOpt oci.SpecOpts, done func() error, rErr error) {
|
||||
var cleanups []func() error
|
||||
done = func() (allErr error) {
|
||||
done = func() error {
|
||||
var errs []error
|
||||
for i := len(cleanups) - 1; i >= 0; i-- {
|
||||
if err := cleanups[i](); err != nil {
|
||||
allErr = multierror.Append(allErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
defer func() {
|
||||
if rErr != nil {
|
||||
|
|
153
cmd/go.mod
153
cmd/go.mod
|
@ -1,89 +1,83 @@
|
|||
module github.com/containerd/stargz-snapshotter/cmd
|
||||
|
||||
go 1.22.7
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.2
|
||||
|
||||
require (
|
||||
github.com/containerd/containerd/api v1.8.0
|
||||
github.com/containerd/containerd/v2 v2.0.0
|
||||
github.com/containerd/go-cni v1.1.10
|
||||
github.com/containerd/containerd/api v1.9.0
|
||||
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.0
|
||||
github.com/containerd/platforms v1.0.0-rc.1
|
||||
github.com/containerd/stargz-snapshotter v0.15.2-0.20240622031358-6405f362966d
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.1
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.17.0
|
||||
github.com/containerd/stargz-snapshotter/ipfs v0.15.2-0.20240622031358-6405f362966d
|
||||
github.com/coreos/go-systemd/v22 v22.5.0
|
||||
github.com/docker/go-metrics v0.0.1
|
||||
github.com/goccy/go-json v0.10.3
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/klauspost/compress v1.17.11
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/opencontainers/runtime-spec v1.2.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/opencontainers/runtime-spec v1.2.1
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/rs/xid v1.6.0
|
||||
github.com/urfave/cli/v2 v2.27.5
|
||||
go.etcd.io/bbolt v1.3.11
|
||||
golang.org/x/sync v0.9.0
|
||||
golang.org/x/sys v0.26.0
|
||||
google.golang.org/grpc v1.68.0
|
||||
k8s.io/cri-api v0.32.0-alpha.0
|
||||
github.com/urfave/cli/v2 v2.27.7
|
||||
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.74.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.12.9 // indirect
|
||||
github.com/Microsoft/hcsshim v0.13.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cilium/ebpf v0.11.0 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.3 // indirect
|
||||
github.com/containerd/console v1.0.4 // indirect
|
||||
github.com/containerd/continuity v0.4.4 // indirect
|
||||
github.com/cilium/ebpf v0.16.0 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.5 // indirect
|
||||
github.com/containerd/console v1.0.5 // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
github.com/containerd/go-runc v1.1.0 // indirect
|
||||
github.com/containerd/plugin v1.0.0 // indirect
|
||||
github.com/containerd/ttrpc v1.2.6 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.2 // indirect
|
||||
github.com/containernetworking/cni v1.2.3 // indirect
|
||||
github.com/containernetworking/plugins v1.5.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // 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
|
||||
github.com/containernetworking/plugins v1.7.1 // indirect
|
||||
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 v27.3.1+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.7.0 // 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.19.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hanwen/go-fuse/v2 v2.6.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hanwen/go-fuse/v2 v2.8.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/intel/goresctrl v0.8.0 // indirect
|
||||
github.com/ipfs/go-cid v0.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/mdlayher/socket v0.5.1 // indirect
|
||||
github.com/mdlayher/vsock v1.2.1 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
|
@ -92,64 +86,69 @@ require (
|
|||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.1 // indirect
|
||||
github.com/moby/sys/symlink v0.3.0 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
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.14.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
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
|
||||
github.com/opencontainers/selinux v1.11.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/opencontainers/selinux v1.12.0 // indirect
|
||||
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.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.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
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
github.com/vbatts/tar-split v0.11.6 // indirect
|
||||
github.com/vbatts/tar-split v0.12.1 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
|
||||
go.opentelemetry.io/otel v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.31.0 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/term v0.25.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // 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.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.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-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
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.31.2 // indirect
|
||||
k8s.io/apimachinery v0.31.2 // indirect
|
||||
k8s.io/client-go v0.31.2 // indirect
|
||||
k8s.io/api v0.33.3 // indirect
|
||||
k8s.io/apimachinery v0.33.3 // indirect
|
||||
k8s.io/client-go v0.33.3 // indirect
|
||||
k8s.io/cri-api v0.33.3 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
tags.cncf.io/container-device-interface v0.8.0 // indirect
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0 // indirect
|
||||
tags.cncf.io/container-device-interface v1.0.1 // indirect
|
||||
tags.cncf.io/container-device-interface/specs-go v1.0.0 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
|
|
337
cmd/go.sum
337
cmd/go.sum
|
@ -1,13 +1,11 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||
github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
|
||||
github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
|
@ -19,48 +17,48 @@ github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2y
|
|||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
|
||||
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
|
||||
github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok=
|
||||
github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
|
||||
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
|
||||
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
||||
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
|
||||
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
|
||||
github.com/containerd/containerd/v2 v2.0.0 h1:qLDdFaAykQrIyLiqwQrNLLz95wiC36bAZVwioUwqShM=
|
||||
github.com/containerd/containerd/v2 v2.0.0/go.mod h1:j25kDy9P48/ngb1sxWIFfK6GsnqOHoSqo1EpAod20VQ=
|
||||
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
|
||||
github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
||||
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
||||
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.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=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
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.10 h1:c2U73nld7spSWfiJwSh/8W9DK+/qQwYM2rngIhCyhyg=
|
||||
github.com/containerd/go-cni v1.1.10/go.mod h1:/Y/sL8yqYQn1ZG1om1OncJB1W4zN3YmjfP/ShCzG/OY=
|
||||
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=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v1.0.0-rc.0 h1:GuHWSKgVVO3POn6nRBB4sH63uPOLa87yuuhsGLWaXAA=
|
||||
github.com/containerd/platforms v1.0.0-rc.0/go.mod h1:T1XAzzOdYs3it7l073MNXyxRwQofJfqwi/8cRjufIk4=
|
||||
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
|
||||
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
|
||||
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
|
||||
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
|
||||
github.com/containerd/ttrpc v1.2.6 h1:zG+Kn5EZ6MUYCS1t2Hmt2J4tMVaLSFEJVOraDQwNPC4=
|
||||
github.com/containerd/ttrpc v1.2.6/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.2 h1:3jN/k2ysKuPCsln5Qv8bzR9cxal8XjkxPogJfSNO31k=
|
||||
github.com/containerd/typeurl/v2 v2.2.2/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/containernetworking/cni v1.2.3 h1:hhOcjNVUQTnzdRJ6alC5XF+wd9mfGIUaj8FuJbEslXM=
|
||||
github.com/containernetworking/cni v1.2.3/go.mod h1:DuLgF+aPd3DzcTQTtp/Nvl1Kim23oFKdm2okJzBQA5M=
|
||||
github.com/containernetworking/plugins v1.5.1 h1:T5ji+LPYjjgW0QM+KyrigZbLsZ8jaX+E5J/EcKOE4gQ=
|
||||
github.com/containernetworking/plugins v1.5.1/go.mod h1:MIQfgMayGuHYs0XdNudf31cLLAC+i242hNm6KuDGqCM=
|
||||
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo=
|
||||
github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4=
|
||||
github.com/containernetworking/plugins v1.7.1 h1:CNAR0jviDj6FS5Vg85NTgKWLDzZPfi/lj+VJfhMDTIs=
|
||||
github.com/containernetworking/plugins v1.7.1/go.mod h1:xuMdjuio+a1oVQsHKjr/mgzuZ24leAsqUYRnzGoXHy0=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -68,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 v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ=
|
||||
github.com/docker/cli v27.3.1+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=
|
||||
|
@ -86,32 +84,33 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
|||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
|
||||
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
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 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
|
@ -135,8 +134,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
|||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
@ -144,38 +143,37 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hanwen/go-fuse/v2 v2.6.3 h1:tDcEkLRx93lXu4XyN1/j8Z74VWvhHDl6qU1kNnvFUqI=
|
||||
github.com/hanwen/go-fuse/v2 v2.6.3/go.mod h1:ugNaD/iv5JYyS1Rcvi57Wz7/vrLQJo10mmketmoef48=
|
||||
github.com/hanwen/go-fuse/v2 v2.8.0 h1:wV8rG7rmCz8XHSOwBZhG5YcVqcYjkzivjmbaMafPlAs=
|
||||
github.com/hanwen/go-fuse/v2 v2.8.0/go.mod h1:yE6D2PqWwm3CbYRxFXV9xUd8Md5d6NG0WBs5spCswmI=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||
github.com/intel/goresctrl v0.8.0 h1:N3shVbS3kA1Hk2AmcbHv8805Hjbv+zqsCIZCGktxx50=
|
||||
github.com/intel/goresctrl v0.8.0/go.mod h1:T3ZZnuHSNouwELB5wvOoUJaB7l/4Rm23rJy/wuWJlr0=
|
||||
github.com/ipfs/go-cid v0.1.0 h1:YN33LQulcRHjfom/i25yoOZR4Telp1Hr/2RU3d0PnC0=
|
||||
github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
|
||||
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
|
@ -183,8 +181,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
|||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
|
@ -206,8 +204,10 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
|
||||
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
|
@ -227,8 +227,8 @@ github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0
|
|||
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
|
||||
github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU=
|
||||
github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -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.14.0 h1:bfrHrJhrRuh/NXH5mCnemjpbGjzRw/b+tJFOD41g2tU=
|
||||
github.com/multiformats/go-multiaddr v0.14.0/go.mod h1:6EkVAxtznq2yC3QT5CM1UTAwG0GTP3EWAIcjHuzQ+r4=
|
||||
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=
|
||||
|
@ -262,26 +262,28 @@ github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOEL
|
|||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
|
||||
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI=
|
||||
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
||||
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
|
||||
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -291,58 +293,61 @@ 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.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
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=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
|
||||
github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
|
||||
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
|
||||
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
|
||||
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
|
||||
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
||||
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
|
@ -356,18 +361,26 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC
|
|||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
|
||||
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
|
||||
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
|
||||
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
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.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=
|
||||
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=
|
||||
|
@ -376,18 +389,18 @@ 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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
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-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4=
|
||||
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
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=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -400,19 +413,19 @@ 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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
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.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
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=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -427,18 +440,18 @@ golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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=
|
||||
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=
|
||||
|
@ -447,8 +460,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
|||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -458,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-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
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.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
|
||||
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
|
||||
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=
|
||||
|
@ -476,49 +489,51 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
||||
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
|
||||
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
|
||||
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
|
||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
|
||||
k8s.io/cri-api v0.32.0-alpha.0 h1:Rs9prajcHWZAdy9ueQdD2R+OOnDD3rKYbM9hQ90iEQU=
|
||||
k8s.io/cri-api v0.32.0-alpha.0/go.mod h1:Po3TMAYH/+KrZabi7QiwQI4a692oZcUOUThd/rqwxrI=
|
||||
k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8=
|
||||
k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE=
|
||||
k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA=
|
||||
k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA=
|
||||
k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg=
|
||||
k8s.io/cri-api v0.33.3 h1:aQvK3UxsaVMul4z71lOiblMHdhw9ROaw3Cgg15xDrD4=
|
||||
k8s.io/cri-api v0.33.3/go.mod h1:OLQvT45OpIA+tv91ZrpuFIGY+Y2Ho23poS7n115Aocs=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
|
||||
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
|
||||
tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=
|
||||
tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=
|
||||
tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0=
|
||||
tags.cncf.io/container-device-interface/specs-go v1.0.0 h1:8gLw29hH1ZQP9K1YtAzpvkHCjjyIxHZYzBAvlQ+0vD8=
|
||||
tags.cncf.io/container-device-interface/specs-go v1.0.0/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ=
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containerd/log"
|
||||
|
||||
"github.com/containerd/stargz-snapshotter/cmd/containerd-stargz-grpc/fsopts"
|
||||
fusemanager "github.com/containerd/stargz-snapshotter/fusemanager"
|
||||
"github.com/containerd/stargz-snapshotter/service"
|
||||
"github.com/containerd/stargz-snapshotter/service/keychain/keychainconfig"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fusemanager.RegisterConfigFunc(func(cc *fusemanager.ConfigContext) ([]service.Option, error) {
|
||||
fsConfig := fsopts.Config{
|
||||
EnableIpfs: cc.Config.IPFS,
|
||||
MetadataStore: cc.Config.MetadataStore,
|
||||
OpenBoltDB: cc.OpenBoltDB,
|
||||
}
|
||||
fsOpts, err := fsopts.ConfigFsOpts(cc.Ctx, cc.RootDir, &fsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []service.Option{service.WithFilesystemOptions(fsOpts...)}, nil
|
||||
})
|
||||
|
||||
fusemanager.RegisterConfigFunc(func(cc *fusemanager.ConfigContext) ([]service.Option, error) {
|
||||
keyChainConfig := keychainconfig.Config{
|
||||
EnableKubeKeychain: cc.Config.Config.KubeconfigKeychainConfig.EnableKeychain,
|
||||
EnableCRIKeychain: cc.Config.Config.CRIKeychainConfig.EnableKeychain,
|
||||
KubeconfigPath: cc.Config.Config.KubeconfigPath,
|
||||
DefaultImageServiceAddress: cc.Config.DefaultImageServiceAddress,
|
||||
ImageServicePath: cc.Config.Config.ImageServicePath,
|
||||
}
|
||||
if cc.Config.Config.CRIKeychainConfig.EnableKeychain && cc.Config.Config.ListenPath == "" || cc.Config.Config.ListenPath == cc.Address {
|
||||
return nil, fmt.Errorf("listen path of CRI server must be specified as a separated socket from FUSE manager server")
|
||||
}
|
||||
// For CRI keychain, if listening path is different from stargz-snapshotter's socket, prepare for the dedicated grpc server and the socket.
|
||||
serveCRISocket := cc.Config.Config.CRIKeychainConfig.EnableKeychain && cc.Config.Config.ListenPath != "" && cc.Config.Config.ListenPath != cc.Address
|
||||
if serveCRISocket {
|
||||
cc.CRIServer = grpc.NewServer()
|
||||
}
|
||||
credsFuncs, err := keychainconfig.ConfigKeychain(cc.Ctx, cc.CRIServer, &keyChainConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if serveCRISocket {
|
||||
addr := cc.Config.Config.ListenPath
|
||||
// Prepare the directory for the socket
|
||||
if err := os.MkdirAll(filepath.Dir(addr), 0700); err != nil {
|
||||
return nil, fmt.Errorf("failed to create directory %q: %w", filepath.Dir(addr), err)
|
||||
}
|
||||
|
||||
// Try to remove the socket file to avoid EADDRINUSE
|
||||
if err := os.RemoveAll(addr); err != nil {
|
||||
return nil, fmt.Errorf("failed to remove %q: %w", addr, err)
|
||||
}
|
||||
|
||||
// Listen and serve
|
||||
l, err := net.Listen("unix", addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error on listen socket %q: %w", addr, err)
|
||||
}
|
||||
go func() {
|
||||
if err := cc.CRIServer.Serve(l); err != nil {
|
||||
log.G(cc.Ctx).WithError(err).Errorf("error on serving CRI via socket %q", addr)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return []service.Option{service.WithCredsFuncs(credsFuncs...)}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
fusemanager.Run()
|
||||
}
|
|
@ -108,7 +108,7 @@ func main() {
|
|||
// Get configuration from specified file
|
||||
if *configPath != "" {
|
||||
tree, err := toml.LoadFile(*configPath)
|
||||
if err != nil && !(os.IsNotExist(err) && *configPath == defaultConfigPath) {
|
||||
if err != nil && (!os.IsNotExist(err) || *configPath != defaultConfigPath) {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to load config file %q", *configPath)
|
||||
}
|
||||
if err := tree.Unmarshal(&config); err != nil {
|
||||
|
@ -122,9 +122,9 @@ func main() {
|
|||
|
||||
// Prepare kubeconfig-based keychain if required
|
||||
credsFuncs := []resolver.Credential{sk.credentials}
|
||||
if config.KubeconfigKeychainConfig.EnableKeychain {
|
||||
if config.EnableKeychain {
|
||||
var opts []kubeconfig.Option
|
||||
if kcp := config.KubeconfigKeychainConfig.KubeconfigPath; kcp != "" {
|
||||
if kcp := config.KubeconfigPath; kcp != "" {
|
||||
opts = append(opts, kubeconfig.WithKubeconfigPath(kcp))
|
||||
}
|
||||
credsFuncs = append(credsFuncs, kubeconfig.NewKubeconfigKeychain(ctx, opts...))
|
||||
|
@ -140,7 +140,7 @@ func main() {
|
|||
Fatalf("failed to prepare mountpoint %q", mountPoint)
|
||||
}
|
||||
}
|
||||
if config.Config.DisableVerification {
|
||||
if config.DisableVerification {
|
||||
log.G(ctx).Fatalf("content verification can't be disabled")
|
||||
}
|
||||
mt, err := getMetadataStore(*rootDir, config)
|
||||
|
@ -151,7 +151,7 @@ func main() {
|
|||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to prepare pool")
|
||||
}
|
||||
if err := store.Mount(ctx, mountPoint, layerManager, config.Config.Debug); err != nil {
|
||||
if err := store.Mount(ctx, mountPoint, layerManager, config.Debug); err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to mount fs at %q", mountPoint)
|
||||
}
|
||||
defer func() {
|
||||
|
@ -266,7 +266,7 @@ func (sk *storeKeychain) credentials(host string, refspec reference.Spec) (strin
|
|||
if acfg, ok := sk.config[refspec.String()]; ok {
|
||||
if acfg.IdentityToken != "" {
|
||||
return "", acfg.IdentityToken, nil
|
||||
} else if !(acfg.Username == "" && acfg.Password == "") {
|
||||
} else if acfg.Username != "" || acfg.Password != "" {
|
||||
return acfg.Username, acfg.Password, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ We assume that you are using containerd (> v1.4.2) as a CRI runtime.
|
|||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
|
||||
```
|
||||
|
||||
|
@ -145,6 +147,8 @@ We assume that you are using CRI-O newer than https://github.com/cri-o/cri-o/pul
|
|||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
```
|
||||
|
||||
- Install fuse
|
||||
|
@ -174,3 +178,7 @@ We assume that you are using CRI-O newer than https://github.com/cri-o/cri-o/pul
|
|||
systemctl restart containerd
|
||||
systemctl restart docker
|
||||
```
|
||||
|
||||
## Using stargz-snapshotter on Lima
|
||||
|
||||
See [`./lima.md`](./lima.md)
|
||||
|
|
|
@ -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.
|
||||
|
@ -268,3 +269,38 @@ ctr-remote image optimize --oci \
|
|||
By default, when the source image is a multi-platform image, `ctr-remote` converts the image corresponding to the platform where `ctr-remote` runs.
|
||||
|
||||
Note that though the images specified by `--all-platform` and `--platform` are converted to eStargz, images that don't correspond to the current platform aren't *optimized*. That is, these images are lazily pulled but without prefetch.
|
||||
|
||||
### Dump log of accessed files during optimization (`--record-out`)
|
||||
|
||||
You can dump the information of which files are accesssed during optimization, using `--record-out` flag.
|
||||
|
||||
For example, the following dumps logs of files accessed during running `ls` in `ubuntu:24.04`.
|
||||
|
||||
```
|
||||
ctr-remote image pull docker.io/library/ubuntu:24.04
|
||||
ctr-remote image optimize --record-out=/tmp/log.json \
|
||||
--entrypoint='[ "/bin/bash", "-c" ]' --args='[ "ls" ]' \
|
||||
docker.io/library/ubuntu:24.04 registry2:5000/ubuntu:24.04
|
||||
```
|
||||
|
||||
The following is the contents of the log (`/tmp/log.json`):
|
||||
|
||||
```
|
||||
{"path":"usr/bin/bash","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"usr/bin/bash","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"etc/ld.so.cache","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"usr/lib/x86_64-linux-gnu/libtinfo.so.6.4","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"usr/lib/x86_64-linux-gnu/libc.so.6","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"etc/nsswitch.conf","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"etc/nsswitch.conf","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"etc/passwd","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"usr/bin/ls","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"etc/ld.so.cache","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"usr/lib/x86_64-linux-gnu/libselinux.so.1","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"usr/lib/x86_64-linux-gnu/libc.so.6","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
{"path":"usr/lib/x86_64-linux-gnu/libpcre2-8.so.0.11.2","manifestDigest":"sha256:5d070ad5f7fe63623cbb99b4fc0fd997f5591303d4b03ccce50f403957d0ddc4","layerIndex":0}
|
||||
```
|
||||
|
||||
For creating an optimized eStargz using this log, you can input this log into [`--estargz-record-in` or `--zstdchunked-record-in` of `nerdctl image convert`](https://github.com/containerd/nerdctl/blob/8b814ca7fe29cb505a02a3d85ba22860e63d15bf/docs/command-reference.md#nerd_face-nerdctl-image-convert) or the same flags for `ctr-remote image convert` .
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
|
@ -0,0 +1,52 @@
|
|||
# Getting started with Stargz Snapshotter on Lima
|
||||
|
||||
[Lima](https://github.com/lima-vm/lima) is a tool to manage Linux virtual machines on various hosts, including MacOS and Linux.
|
||||
Lima can be used as an easy way to get started with Stargz Snapshotter as Lima provides a default VM image bundling [containerd](https://github.com/containerd/containerd), [nerdctl](https://github.com/containerd/nerdctl)(Docker-compatible CLI of containerd) and Stargz Snapshotter.
|
||||
|
||||
This document describes how to get started with Stargz Snapshotter on Lima.
|
||||
|
||||
## Enable Stargz Snapshotter using `--snapshotter=stargz` flag
|
||||
|
||||
nerdctl's `--snapshotter=stargz` flag enables stargz-snapshotter.
|
||||
|
||||
```
|
||||
$ nerdctl.lima --snapshotter=stargz system info | grep stargz
|
||||
Storage Driver: stargz
|
||||
```
|
||||
|
||||
Using this flag, you can perform lazy pulling of a python eStargz image and run it.
|
||||
|
||||
```
|
||||
$ nerdctl.lima --snapshotter=stargz run --rm -it --name python ghcr.io/stargz-containers/python:3.13-esgz
|
||||
Python 3.13.2 (main, Feb 6 2025, 22:37:13) [GCC 12.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>>
|
||||
```
|
||||
|
||||
## Use Stargz Snapshotter as the default snapshotter
|
||||
|
||||
nerdctl recognizes an environment variable `CONTAINERD_SNAPSHOTTER` for the snapshotter to use.
|
||||
You can add this environment variable to the VM by configuring Lima config as shown in the following:
|
||||
|
||||
```
|
||||
$ cat <<EOF >> ~/.lima/_config/override.yaml
|
||||
env:
|
||||
CONTAINERD_SNAPSHOTTER: stargz
|
||||
EOF
|
||||
$ limactl stop
|
||||
$ limactl start
|
||||
$ nerdctl.lima system info | grep Storage
|
||||
Storage Driver: stargz
|
||||
```
|
||||
|
||||
> NOTE: `override.yaml` applies to all the instances of Lima
|
||||
|
||||
|
||||
You can perform lazy pulling of eStargz using nerdctl, without any extra flags.
|
||||
|
||||
```
|
||||
$ nerdctl.lima run --rm -it --name python ghcr.io/stargz-containers/python:3.13-esgz
|
||||
Python 3.13.2 (main, Feb 6 2025, 22:37:13) [GCC 12.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>>
|
||||
```
|
|
@ -1,22 +1,22 @@
|
|||
# Containerd Stargz Snapshotter Plugin Overview
|
||||
|
||||
__Before get through this overview document, we recommend you to read [README](../README.md).__
|
||||
__Before reading this overview document, we recommend you read [README](../README.md).__
|
||||
|
||||
Pulling image is one of the time-consuming steps in the container startup process.
|
||||
In containerd community, we have had a lot of discussions to address this issue as the following,
|
||||
Pulling images is one of the most time-consuming steps in the container startup process.
|
||||
In the containerd community, we have had a lot of discussions to address this issue at the following:
|
||||
|
||||
- [#3731 Support remote snapshotter to speed up image pulling](https://github.com/containerd/containerd/issues/3731)
|
||||
- [#2968 Support `Prepare` for existing snapshots in Snapshotter interface](https://github.com/containerd/containerd/issues/2968)
|
||||
- [#2943 remote filesystem snapshotter](https://github.com/containerd/containerd/issues/2943)
|
||||
|
||||
The solution for the fast image distribution is called *Remote Snapshotter* plugin.
|
||||
This prepares container's rootfs layers by directly mounting from remote stores instead of downloading and unpacking the entire image contents.
|
||||
The actual image contents can be fetched *lazily* so runtimes can startup containers before the entire image contents to be locally available.
|
||||
We call these remotely mounted layers as *remote snapshots*.
|
||||
The solution for fast image distribution is called *Remote Snapshotter* plugin.
|
||||
This prepares the container's rootfs layers by directly mounting from remote stores instead of downloading and unpacking the entire image contents.
|
||||
The actual image contents can be fetched *lazily* so runtimes can start containers before the entire image contents are locally available.
|
||||
We call these remotely mounted layers *remote snapshots*.
|
||||
|
||||
*Stargz Snapshotter* is a remote snapshotter plugin implementation which supports standard compatible remote snapshots functionality.
|
||||
This snapshotter leverages [eStargz](/docs/stargz-estargz.md) image, which is lazily-pullable and still standard-compatible.
|
||||
Because of this compatibility, eStargz image can be pushed to and lazily pulled from [OCI](https://github.com/opencontainers/distribution-spec)/[Docker](https://docs.docker.com/registry/spec/api/) registries (e.g. ghcr.io).
|
||||
Because of this compatibility, eStargz images can be pushed to and lazily pulled from [OCI](https://github.com/opencontainers/distribution-spec)/[Docker](https://docs.docker.com/registry/spec/api/) registries (e.g. ghcr.io).
|
||||
Furthermore, images can run even on eStargz-agnostic runtimes (e.g. Docker).
|
||||
When you run a container image and it is formatted by eStargz, stargz snapshotter prepares container's rootfs layers as remote snapshots by mounting layers from the registry to the node, instead of pulling the entire image contents.
|
||||
|
||||
|
@ -27,10 +27,10 @@ This document gives you a high-level overview of stargz snapshotter.
|
|||
## Stargz Snapshotter proxy plugin
|
||||
|
||||
Stargz snapshotter is implemented as a [proxy plugin](https://github.com/containerd/containerd/blob/04985039cede6aafbb7dfb3206c9c4d04e2f924d/PLUGINS.md#proxy-plugins) daemon (`containerd-stargz-grpc`) for containerd.
|
||||
When containerd starts a container, it queries the rootfs snapshots to stargz snapshotter daemon through an unix socket.
|
||||
When containerd starts a container, it queries the rootfs snapshots to stargz snapshotter daemon through a unix socket.
|
||||
This snapshotter remotely mounts queried eStargz layers from registries to the node and provides these mount points as remote snapshots to containerd.
|
||||
|
||||
Containerd recognizes this plugin through an unix socket specified in the configuration file (e.g. `/etc/containerd/config.toml`).
|
||||
Containerd recognizes this plugin through a unix socket specified in the configuration file (e.g. `/etc/containerd/config.toml`).
|
||||
Stargz snapshotter can also be used through Kubernetes CRI by specifying the snapshotter name in the CRI plugin configuration.
|
||||
We assume that you are using containerd (> v1.4.2).
|
||||
|
||||
|
@ -44,6 +44,8 @@ version = 2
|
|||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
|
||||
# Use stargz snapshotter through CRI
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
|
@ -51,24 +53,26 @@ version = 2
|
|||
disable_snapshot_annotations = false
|
||||
```
|
||||
|
||||
> NOTE: `root` field of `proxy_plugins` is needed for the CRI plugin to recognize stargz snapshotter's root directory.
|
||||
|
||||
This repo contains [a Dockerfile as a KinD node image](/Dockerfile) which includes the above configuration.
|
||||
|
||||
## State directory
|
||||
|
||||
Stargz snapshotter mounts eStargz layers from registries to the node using FUSE.
|
||||
The all files metadata in the image are preserved on the filesystem and files contents are fetched from registries on demand.
|
||||
Metadata for all files in the image are preserved on the container filesystem and the file contents are fetched from registries on demand.
|
||||
|
||||
At the root of the filesystem, there is a *state directory* (`/.stargz-snapshotter`) for status monitoring for the filesystem.
|
||||
At the root of the container filesystem, there is a *state directory* (`/.stargz-snapshotter`) for status monitoring for the filesystem.
|
||||
This directory is hidden from `getdents(2)` so you can't see this with `ls -a /`.
|
||||
Instead, you can directly access the directory by specifying the path (`/.stargz-snapshotter`).
|
||||
|
||||
State directory contains JSON-formatted metadata files for each layer.
|
||||
The state directory contains JSON-formatted metadata files for each layer.
|
||||
In the following example, metadata JSON files for overlayed 7 layers are visible.
|
||||
In each metadata JSON file, the following fields are contained,
|
||||
In each metadata JSON file, the following fields are contained:
|
||||
|
||||
- `digest` contains the layer digest. This is the same value as that in the image's manifest.
|
||||
- `size` is the size bytes of the layer.
|
||||
- `fetchedSize` and `fetchedPercent` indicate how many bytes have been fetched for this layer. Stargz snapshotter aggressively downloads this layer in the background - unless configured otherwise - so these values gradually increase. When `fetchedPercent` reaches to `100` percents, this layer has been fully downloaded on the node and no further access will occur for reading files.
|
||||
- `fetchedSize` and `fetchedPercent` indicate how many bytes have been fetched for this layer. Stargz snapshotter aggressively downloads this layer in the background - unless configured otherwise - so these values gradually increase. When `fetchedPercent` reaches `100` percent, this layer has been fully downloaded on the node and no further access will occur for reading files.
|
||||
|
||||
Note that the state directory layout and the metadata JSON structure are subject to change.
|
||||
|
||||
|
@ -95,6 +99,59 @@ root@1d43741b8d29:/go# cat /.stargz-snapshotter/*
|
|||
{"digest":"sha256:f077511be7d385c17ba88980379c5cd0aab7068844dffa7a1cefbf68cc3daea3","size":580,"fetchedSize":580,"fetchedPercent":100}
|
||||
```
|
||||
|
||||
## Fuse Manager
|
||||
|
||||
The fuse manager is designed to maintain the availability of running containers by managing the lifecycle of FUSE mountpoints independently from the stargz snapshotter.
|
||||
|
||||
### Fuse Manager Overview
|
||||
Remote snapshots are mounted using FUSE, and its filesystem processes are attached to the stargz snapshotter. If the stargz snapshotter restarts (due to configuration changes or crashes), all filesystem processes will be killed and restarted, which causes the remount of FUSE mountpoints, making running containers unavailable.
|
||||
|
||||
To avoid this, we use a fuse daemon called the fuse manager to handle filesystem processes. The fuse manager is responsible for mounting and unmounting remote snapshotters. Its process is detached from the stargz snapshotter main process to an independent one in a shim-like way during the snapshotter's startup. This design ensures that the restart of the snapshotter won't affect the filesystem processes it manages, keeping mountpoints and running containers available during the restart. However, it is important to note that the restart of the fuse manager itself triggers a remount, so it is recommended to keep the fuse manager running in a good state.
|
||||
|
||||
You can enable the fuse manager by adding the following configuration.
|
||||
|
||||
```toml
|
||||
[fusem_anager]
|
||||
enable = true
|
||||
```
|
||||
|
||||
## Killing and restarting Stargz Snapshotter
|
||||
|
||||
Stargz Snapshotter works as a FUSE server for the snapshots.
|
||||
When you stop Stargz Sanpshotter on the node, it takes the following behaviour depending on the configuration.
|
||||
|
||||
### FUSE manager mode is disabled
|
||||
|
||||
killing containerd-stargz-grpc will result in unmounting all snapshot mounts managed by Stargz Snapshotter.
|
||||
When containerd-stargz-grpc is restarted, all those snapshots are mounted again by lazy pulling all layers.
|
||||
If the snapshotter fails to mount one of the snapshots (e.g. because of lazy pulling failure) during this step, the behaviour differs depending on `allow_invalid_mounts_on_restart` flag in the config TOML.
|
||||
|
||||
- `allow_invalid_mounts_on_restart = true`: containerd-stargz-grpc leaves the failed snapshots as empty directories. The user needs to manually remove those snapshot via containerd (e.g. using `ctr snapshot rm` command). The name of those snapshots can be seen in the log with `failed to restore remote snapshot` message.
|
||||
|
||||
- `allow_invalid_mounts_on_restart = false`: containerd-stargz-grpc doesn't start. The user needs to manually recover this (e.g. by wiping snapshotter and containerd state).
|
||||
|
||||
### FUSE manager mode is enabled
|
||||
|
||||
Killing containerd-stargz-grpc using non-SIGINT signal (e.g. using SIGTERM) doesn't affect the snapshot mounts because the FUSE manager process detached from containerd-stargz-grpc keeps on serving FUSE mounts to the kernel.
|
||||
This is useful when you reload the updated config TOML to Stargz Snapshotter without unmounting existing snapshots.
|
||||
|
||||
FUSE manager serves FUSE mounts of the snapshots so if you kill this process, all snapshot mounts will be unavailable.
|
||||
When stopping FUSE manager for upgrading the binary or restarting the node, you can use SIGINT signal to trigger the graceful exit as shown in the following steps.
|
||||
|
||||
1. Stop containers that use Stargz Snapshotter. Stopping FUSE manager makes all snapshot mounts unavailable so containers can't keep working.
|
||||
2. Stop containerd-stargz-grpc process using SIGINT. This signal triggers unmounting of all snapshots and cleaning up of the associated resources.
|
||||
3. Kill the FUSE manager process (`stargz-fuse-manager`)
|
||||
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.
|
||||
|
@ -129,6 +186,9 @@ The snapshotter acquires registry creds by scanning requests.
|
|||
|
||||
You must specify `--image-service-endpoint=unix:///run/containerd-stargz-grpc/containerd-stargz-grpc.sock` option to kubelet.
|
||||
|
||||
You can specify the backing image service's socket using `image_service_path`.
|
||||
The default is the containerd's socket (`/run/containerd/containerd.sock`).
|
||||
|
||||
```toml
|
||||
# Stargz Snapshotter proxies CRI Image Service into containerd socket.
|
||||
[cri_keychain]
|
||||
|
@ -136,6 +196,13 @@ enable_keychain = true
|
|||
image_service_path = "/run/containerd/containerd.sock"
|
||||
```
|
||||
|
||||
The default path where containerd-stargz-grpc serves the CRI Image Service API is `unix:///run/containerd-stargz-grpc/containerd-stargz-grpc.sock`.
|
||||
You can also change this path using `listen_path` field.
|
||||
|
||||
> Note that if you enabled the FUSE manager and CRI-based authentication together, `listen_path` is a mandatory field with some caveats:
|
||||
> - This path must be different from the FUSE manager's socket path (`/run/containerd-stargz-grpc/fuse-manager.sock`) because they have different lifecycle. Specifically, the CRI socket is recreted on each reload of the configuration to the FUSE manager.
|
||||
> - containerd-stargz-grpc's socket path (`/run/containerd-stargz-grpc/containerd-stargz-grpc.sock`) can't be used as `listen_path` because the CRI socket is served by the FUSE manager process (not containerd-stargz-grpc process).
|
||||
|
||||
#### kubeconfig-based authentication
|
||||
|
||||
This is another way to enable lazy pulling of private images on Kubernetes.
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# Introduction
|
||||
|
||||
FUSE Passthrough has been introduced in the Linux kernel version 6.9 ([Linux Kernel Commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6ce8b2ce0d7e3a621cdc9eb66d74436ca7d0e66e)). This feature has shown significant performance improvements, as detailed in the following articles:
|
||||
|
||||
[Phoronix Article on FUSE Passthrough](https://www.phoronix.com/news/FUSE-Passthrough-In-6.9-Next)<br>
|
||||
|
||||
FUSE Passthrough allows performing read and write (also via memory maps) on a backing file without incurring the overhead of roundtrips to userspace.
|
||||
|
||||

|
||||
|
||||
Additionally, the `go-fuse` package, which Stargz-Snapshotter depends on, has also added support for this passthrough feature:
|
||||
|
||||
[go-fuse Commit 1](https://github.com/hanwen/go-fuse/commit/e0641a46c6cca7e5370fc135f78caf7cb7fc3aa8#diff-f830ac3db25844bf71102b09e4e02f7213e9cdb577b32745979d61d775462bd3R157)<br>
|
||||
[go-fuse Commit 2](https://github.com/hanwen/go-fuse/commit/e0a0b09ae8287249c38033a27fd69a3593c7e235#diff-1521152f1fc3600273bda897c669523dc1e9fc9cbe24046838f043a8040f0d67R749)<br>
|
||||
[go-fuse Commit 3](https://github.com/hanwen/go-fuse/commit/1a7d98b0360f945fca50ac79905332b7106c049f)
|
||||
|
||||
When a user-defined file implements the `FilePassthroughFder` interface, `go-fuse` will attempt to register the file `fd` from the file with the kernel.
|
||||
|
||||
# Configuration
|
||||
|
||||
## Basic Configuration
|
||||
|
||||
To enable FUSE passthrough mode, first verify that your host's kernel supports this feature. You can check this by running the following command:
|
||||
|
||||
```bash
|
||||
$ cat /boot/config-$(uname -r) | grep "CONFIG_FUSE_PASSTHROUGH=y"
|
||||
CONFIG_FUSE_PASSTHROUGH=y
|
||||
```
|
||||
|
||||
Once you have confirmed kernel support, you need to enable passthrough mode in your `config.toml` file with the following configuration:
|
||||
|
||||
```toml
|
||||
[fuse]
|
||||
passthrough = true
|
||||
```
|
||||
|
||||
After updating the configuration, specify the `config.toml` file when starting `containerd-stargz-grpc` and restart the service:
|
||||
|
||||
```bash
|
||||
$ containerd-stargz-grpc -config config.toml
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
In passthrough mode, the initial pull of an image requires merging chunks into a file. This process can be time-consuming, especially for large files.
|
||||
|
||||
To optimize the time taken for the initial image pull, you can use the `merge_buffer_size` and `merge_worker_count` configuration options. The `merge_buffer_size` specifies the size of the buffer used for reading the image, with a default value of 400MB. The `merge_worker_count` determines the level of concurrency for reading the image, with a default value of 10.
|
||||
|
||||
By concurrently reading chunks and caching them for batch writing, you can significantly enhance the performance of the initial image pull in passthrough mode.
|
||||
|
||||
# Important Considerations
|
||||
|
||||
When passthrough mode is enabled, the following configuration is applied by default, even if it is set to false in the configuration file:
|
||||
|
||||
```toml
|
||||
[directory_cache]
|
||||
direct = true
|
||||
```
|
||||
|
||||
This is because, in passthrough mode, read operations after opening a file are handled directly by the kernel.
|
|
@ -0,0 +1,99 @@
|
|||
# Enabling Stargz Snapshotter With Transfer Service
|
||||
|
||||
Transfer Service is a containerd component which is used for image management in contianerd (e.g. pulling and pushing images).
|
||||
For details about Transfer Service, refer to [the official document in the containerd repo](https://github.com/containerd/containerd/blob/6af7c07905a317d4c343a49255e2392f4c8569f9/docs/transfer.md).
|
||||
|
||||
To use Stargz Snapshotter on containerd with enabling Transfer Service, additional configurations is needed.
|
||||
|
||||
## Availability of Transfer Service
|
||||
|
||||
Transfer Service is available since v1.7.
|
||||
And this is enabled in different settings depending on the containerd version.
|
||||
|
||||
|containerd version|`ctr`|CRI|
|
||||
---|---|---
|
||||
|containerd >= v1.7 and < v2.0|Disabled by default. Enabled by `--local=false`|Disabled|
|
||||
|containerd >= v2.0 and < v2.1|Enabled by default. Disabled by `--local`|Disabled|
|
||||
|containerd >= v2.1|Enabled by default. Disabled by `--local`|Enabled by default. Disabled when conditions described in [containerd's CRI document](https://github.com/containerd/containerd/blob/v2.1.0/docs/cri/config.md#image-pull-configuration-since-containerd-v21) are met|
|
||||
|
||||
### Note about containerd v2.1
|
||||
|
||||
Before containerd v2.1, `disable_snapshot_annotations = false` in containerd's config TOML was a mandatory field to enable Stargz Snapshotter in CRI.
|
||||
In containerd v2.1, `disable_snapshot_annotations = false` field can still be used to enable Stargz Snapshotter and containerd disables Transfer Service when this field is detected.
|
||||
If you want to enable Transfer Service, you need to remove `disable_snapshot_annotations = false` field and apply the configuration explaind in this document.
|
||||
|
||||
## How to enable Stargz Snapshotter when Transfer Service is enabled?
|
||||
|
||||
In containerd v2.1, Transfer Service added support for remote snapshotters like Stargz Snapshotter.
|
||||
|
||||
### For ctr and other non-CRI clients
|
||||
|
||||
To enable Stargz Snapshotter with Transfer Service, you need to start containerd-stargz-grpc on the node and add the following configuration to contianerd's config TOML file.
|
||||
Note that you need to add a field `enable_remote_snapshot_annotations = "true"` in `proxy_plugins.stargz.exports` so that containerd can correctly pass image-related information to Stargz Snapshotter.
|
||||
|
||||
```toml
|
||||
version = 2
|
||||
|
||||
# Enable Stargz Snapshotter in Transfer Service
|
||||
[[plugins."io.containerd.transfer.v1.local".unpack_config]]
|
||||
platform = "linux"
|
||||
snapshotter = "stargz"
|
||||
|
||||
# Plugin Stargz Snapshotter
|
||||
[proxy_plugins]
|
||||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
enable_remote_snapshot_annotations = "true"
|
||||
```
|
||||
|
||||
#### Example client command
|
||||
|
||||
When you enable Transfer Service with Stargz Snapshotter, you can perform lazy pulling using the normal `ctr` command. (of course, `ctr-remote` can still be used)
|
||||
|
||||
```
|
||||
# ctr image pull --snapshotter=stargz ghcr.io/stargz-containers/ubuntu:24.04-esgz
|
||||
```
|
||||
|
||||
Then `mount | grep stargz` prints stargz mounts on the node.
|
||||
|
||||
### For CRI
|
||||
|
||||
To enable Stargz Snapshotter with Transfer Service, you need to start containerd-stargz-grpc on the node and add the following configuration to contianerd's config TOML file.
|
||||
|
||||
```toml
|
||||
version = 2
|
||||
|
||||
# Basic CRI configuration with enabling Stargz Snapshotter
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
default_runtime_name = "runc"
|
||||
snapshotter = "stargz"
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
|
||||
# Enable Stargz Snapshotter in Transfer Service
|
||||
[[plugins."io.containerd.transfer.v1.local".unpack_config]]
|
||||
platform = "linux"
|
||||
snapshotter = "stargz"
|
||||
|
||||
# Plugin Stargz Snapshotter
|
||||
[proxy_plugins]
|
||||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
enable_remote_snapshot_annotations = "true"
|
||||
```
|
||||
|
||||
#### Example client command
|
||||
|
||||
You can quickly check the behaviour using `crictl` command.
|
||||
|
||||
```
|
||||
# crictl image pull ghcr.io/stargz-containers/ubuntu:24.04-esgz
|
||||
```
|
||||
|
||||
Then `mount | grep stargz` prints stargz mounts on the node.
|
|
@ -408,11 +408,11 @@ func readerFromEntries(entries ...*entry) io.Reader {
|
|||
defer tw.Close()
|
||||
for _, entry := range entries {
|
||||
if err := tw.WriteHeader(entry.header); err != nil {
|
||||
pw.CloseWithError(fmt.Errorf("Failed to write tar header: %v", err))
|
||||
pw.CloseWithError(fmt.Errorf("failed to write tar header: %v", err))
|
||||
return
|
||||
}
|
||||
if _, err := io.Copy(tw, entry.payload); err != nil {
|
||||
pw.CloseWithError(fmt.Errorf("Failed to write tar payload: %v", err))
|
||||
pw.CloseWithError(fmt.Errorf("failed to write tar payload: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -627,12 +627,12 @@ func (cr *countReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
|||
|
||||
switch whence {
|
||||
default:
|
||||
return 0, fmt.Errorf("Unknown whence: %v", whence)
|
||||
return 0, fmt.Errorf("unknown whence: %v", whence)
|
||||
case io.SeekStart:
|
||||
case io.SeekCurrent:
|
||||
offset += *cr.cPos
|
||||
case io.SeekEnd:
|
||||
return 0, fmt.Errorf("Unsupported whence: %v", whence)
|
||||
return 0, fmt.Errorf("unsupported whence: %v", whence)
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
|
|
|
@ -81,7 +81,7 @@ func TestChunkEntryForOffset(t *testing.T) {
|
|||
if ok != te.wantOk {
|
||||
t.Errorf("ok = %v; want (%v)", ok, te.wantOk)
|
||||
} else if ok {
|
||||
if !(ce.ChunkOffset == te.wantChunkOffset && ce.ChunkSize == te.wantChunkSize) {
|
||||
if ce.ChunkOffset != te.wantChunkOffset || ce.ChunkSize != te.wantChunkSize {
|
||||
t.Errorf("chunkOffset = %d, ChunkSize = %d; want (chunkOffset = %d, chunkSize = %d)",
|
||||
ce.ChunkOffset, ce.ChunkSize, te.wantChunkOffset, te.wantChunkSize)
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ func gzipFooterBytes() ([]byte, error) {
|
|||
header[0], header[1] = 'S', 'G'
|
||||
subfield := "STARGZEXTERNALTOC" // len("STARGZEXTERNALTOC") = 17
|
||||
binary.LittleEndian.PutUint16(header[2:4], uint16(len(subfield))) // little-endian per RFC1952
|
||||
gz.Header.Extra = append(header, []byte(subfield)...)
|
||||
gz.Extra = append(header, []byte(subfield)...)
|
||||
if err := gz.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ func (gz *GzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, t
|
|||
return 0, 0, 0, err
|
||||
}
|
||||
defer zr.Close()
|
||||
extra := zr.Header.Extra
|
||||
extra := zr.Extra
|
||||
si1, si2, subfieldlen, subfield := extra[0], extra[1], extra[2:4], extra[4:]
|
||||
if si1 != 'S' || si2 != 'G' {
|
||||
return 0, 0, 0, fmt.Errorf("invalid subfield IDs: %q, %q; want E, S", si1, si2)
|
||||
|
|
|
@ -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),
|
||||
|
@ -56,15 +71,15 @@ type gzipController struct {
|
|||
}
|
||||
|
||||
func (gc *gzipController) String() string {
|
||||
return fmt.Sprintf("externaltoc_gzip_compression_level=%v", gc.GzipCompressor.compressionLevel)
|
||||
return fmt.Sprintf("externaltoc_gzip_compression_level=%v", gc.compressionLevel)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
module github.com/containerd/stargz-snapshotter/estargz
|
||||
|
||||
go 1.22.0
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.17.11
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/vbatts/tar-split v0.11.6
|
||||
golang.org/x/sync v0.9.0
|
||||
github.com/vbatts/tar-split v0.12.1
|
||||
golang.org/x/sync v0.16.0
|
||||
)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
|
||||
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
||||
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
|
|
|
@ -109,7 +109,7 @@ func gzipFooterBytes(tocOff int64) []byte {
|
|||
header[0], header[1] = 'S', 'G'
|
||||
subfield := fmt.Sprintf("%016xSTARGZ", tocOff)
|
||||
binary.LittleEndian.PutUint16(header[2:4], uint16(len(subfield))) // little-endian per RFC1952
|
||||
gz.Header.Extra = append(header, []byte(subfield)...)
|
||||
gz.Extra = append(header, []byte(subfield)...)
|
||||
gz.Close()
|
||||
if buf.Len() != FooterSize {
|
||||
panic(fmt.Sprintf("footer buffer = %d, not %d", buf.Len(), FooterSize))
|
||||
|
@ -136,7 +136,7 @@ func (gz *GzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, t
|
|||
return 0, 0, 0, err
|
||||
}
|
||||
defer zr.Close()
|
||||
extra := zr.Header.Extra
|
||||
extra := zr.Extra
|
||||
si1, si2, subfieldlen, subfield := extra[0], extra[1], extra[2:4], extra[4:]
|
||||
if si1 != 'S' || si2 != 'G' {
|
||||
return 0, 0, 0, fmt.Errorf("invalid subfield IDs: %q, %q; want E, S", si1, si2)
|
||||
|
@ -181,7 +181,7 @@ func (gz *LegacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOff
|
|||
return 0, 0, 0, fmt.Errorf("legacy: failed to get footer gzip reader: %w", err)
|
||||
}
|
||||
defer zr.Close()
|
||||
extra := zr.Header.Extra
|
||||
extra := zr.Extra
|
||||
if len(extra) != 16+len("STARGZ") {
|
||||
return 0, 0, 0, fmt.Errorf("legacy: invalid stargz's extra field size")
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
@ -52,15 +67,15 @@ type gzipController struct {
|
|||
}
|
||||
|
||||
func (gc *gzipController) String() string {
|
||||
return fmt.Sprintf("gzip_compression_level=%v", gc.GzipCompressor.compressionLevel)
|
||||
return fmt.Sprintf("gzip_compression_level=%v", gc.compressionLevel)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -106,7 +121,7 @@ func checkLegacyFooter(t *testing.T, off int64) {
|
|||
func legacyFooterBytes(tocOff int64) []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, legacyFooterSize))
|
||||
gz, _ := gzip.NewWriterLevel(buf, gzip.NoCompression)
|
||||
gz.Header.Extra = []byte(fmt.Sprintf("%016xSTARGZ", tocOff))
|
||||
gz.Extra = []byte(fmt.Sprintf("%016xSTARGZ", tocOff))
|
||||
gz.Close()
|
||||
if buf.Len() != legacyFooterSize {
|
||||
panic(fmt.Sprintf("footer buffer = %d, not %d", buf.Len(), legacyFooterSize))
|
||||
|
|
|
@ -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,18 +384,19 @@ 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
|
||||
if srcCompression == gzipType {
|
||||
switch srcCompression {
|
||||
case gzipType:
|
||||
w = gzip.NewWriter(buf)
|
||||
} else if srcCompression == zstdType {
|
||||
case zstdType:
|
||||
w, err = zstd.NewWriter(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to init zstd writer: %v", err)
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
return src
|
||||
}
|
||||
src.Seek(0, io.SeekStart)
|
||||
|
@ -386,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)
|
||||
|
@ -445,7 +477,7 @@ func contains(t *testing.T, a, b stargzEntry) bool {
|
|||
bbytes, bnext, bok := readOffset(t, bf, nr, b)
|
||||
if !aok && !bok {
|
||||
break
|
||||
} else if !(aok && bok) || anext != bnext {
|
||||
} else if !aok || !bok || anext != bnext {
|
||||
t.Logf("%q != %q (offset=%d): chunk existence a=%v vs b=%v, anext=%v vs bnext=%v",
|
||||
ae.Name, be.Name, nr, aok, bok, anext, bnext)
|
||||
return false
|
||||
|
@ -497,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
|
||||
|
@ -516,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)
|
||||
|
@ -530,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/"),
|
||||
)
|
||||
|
@ -558,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),
|
||||
|
@ -582,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),
|
||||
|
@ -606,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),
|
||||
|
@ -653,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)
|
||||
|
@ -689,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),
|
||||
|
@ -800,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),
|
||||
|
@ -881,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 {
|
||||
|
@ -901,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
|
||||
|
@ -928,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 {
|
||||
|
@ -957,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 {
|
||||
|
@ -989,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))),
|
||||
|
@ -1046,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)
|
||||
|
@ -1119,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"
|
||||
|
||||
|
@ -1463,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())
|
||||
|
@ -1627,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")
|
||||
}
|
||||
|
@ -1647,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")
|
||||
|
@ -1672,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)
|
||||
|
@ -1694,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" {
|
||||
|
@ -1710,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" {
|
||||
|
@ -1737,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)
|
||||
|
@ -1749,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
|
||||
|
@ -1796,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)
|
||||
|
@ -1813,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)
|
||||
|
@ -1857,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)
|
||||
|
@ -1876,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" {
|
||||
|
@ -1890,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" {
|
||||
|
@ -1908,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 {
|
||||
|
@ -1923,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" {
|
||||
|
@ -1939,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)
|
||||
|
@ -1952,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)
|
||||
|
@ -1966,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 {
|
||||
|
@ -2038,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) {
|
||||
|
@ -2247,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")
|
||||
}
|
||||
|
@ -2317,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
|
||||
}
|
||||
|
@ -2346,8 +2377,8 @@ func CheckGzipHasStreams(t *testing.T, b []byte, streams []int64) {
|
|||
t.Fatalf("countStreams(gzip), Copy: %v", err)
|
||||
}
|
||||
var extra string
|
||||
if len(zr.Header.Extra) > 0 {
|
||||
extra = fmt.Sprintf("; extra=%q", zr.Header.Extra)
|
||||
if len(zr.Extra) > 0 {
|
||||
extra = fmt.Sprintf("; extra=%q", zr.Extra)
|
||||
}
|
||||
t.Logf(" [%d] at %d in stargz, uncompressed length %d%s", numStreams, zoff, n, extra)
|
||||
delete(wants, int64(zoff))
|
||||
|
@ -2355,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 {
|
||||
|
|
|
@ -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),
|
||||
|
@ -50,12 +65,12 @@ type zstdController struct {
|
|||
}
|
||||
|
||||
func (zc *zstdController) String() string {
|
||||
return fmt.Sprintf("zstd_compression_level=%v", zc.Compressor.CompressionLevel)
|
||||
return fmt.Sprintf("zstd_compression_level=%v", zc.CompressionLevel)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
|
@ -37,115 +37,127 @@ const (
|
|||
type Config struct {
|
||||
// Type of cache for compressed contents fetched from the registry. "memory" stores them on memory.
|
||||
// Other values default to cache them on disk.
|
||||
HTTPCacheType string `toml:"http_cache_type"`
|
||||
HTTPCacheType string `toml:"http_cache_type" json:"http_cache_type"`
|
||||
|
||||
// Type of cache for uncompressed files contents. "memory" stores them on memory. Other values
|
||||
// default to cache them on disk.
|
||||
FSCacheType string `toml:"filesystem_cache_type"`
|
||||
FSCacheType string `toml:"filesystem_cache_type" json:"filesystem_cache_type"`
|
||||
|
||||
// ResolveResultEntryTTLSec is TTL (in sec) to cache resolved layers for
|
||||
// future use. (default 120s)
|
||||
ResolveResultEntryTTLSec int `toml:"resolve_result_entry_ttl_sec"`
|
||||
ResolveResultEntryTTLSec int `toml:"resolve_result_entry_ttl_sec" json:"resolve_result_entry_ttl_sec"`
|
||||
|
||||
// PrefetchSize is the default size (in bytes) to prefetch when mounting a layer. Default is 0. Stargz-snapshotter still
|
||||
// uses the value specified by the image using "containerd.io/snapshot/remote/stargz.prefetch" or the landmark file.
|
||||
PrefetchSize int64 `toml:"prefetch_size"`
|
||||
PrefetchSize int64 `toml:"prefetch_size" json:"prefetch_size"`
|
||||
|
||||
// PrefetchTimeoutSec is the default timeout (in seconds) when the prefetching takes long. Default is 10s.
|
||||
PrefetchTimeoutSec int64 `toml:"prefetch_timeout_sec"`
|
||||
PrefetchTimeoutSec int64 `toml:"prefetch_timeout_sec" json:"prefetch_timeout_sec"`
|
||||
|
||||
// NoPrefetch disables prefetching. Default is false.
|
||||
NoPrefetch bool `toml:"noprefetch"`
|
||||
NoPrefetch bool `toml:"noprefetch" json:"noprefetch"`
|
||||
|
||||
// NoBackgroundFetch disables the behaviour of fetching the entire layer contents in background. Default is false.
|
||||
NoBackgroundFetch bool `toml:"no_background_fetch"`
|
||||
NoBackgroundFetch bool `toml:"no_background_fetch" json:"no_background_fetch"`
|
||||
|
||||
// Debug enables filesystem debug log.
|
||||
Debug bool `toml:"debug"`
|
||||
Debug bool `toml:"debug" json:"debug"`
|
||||
|
||||
// AllowNoVerification allows mouting images without verification. Default is false.
|
||||
AllowNoVerification bool `toml:"allow_no_verification"`
|
||||
AllowNoVerification bool `toml:"allow_no_verification" json:"allow_no_verification"`
|
||||
|
||||
// DisableVerification disables verifying layer contents. Default is false.
|
||||
DisableVerification bool `toml:"disable_verification"`
|
||||
DisableVerification bool `toml:"disable_verification" json:"disable_verification"`
|
||||
|
||||
// MaxConcurrency is max number of concurrent background tasks for fetching layer contents. Default is 2.
|
||||
MaxConcurrency int64 `toml:"max_concurrency"`
|
||||
MaxConcurrency int64 `toml:"max_concurrency" json:"max_concurrency"`
|
||||
|
||||
// NoPrometheus disables exposing filesystem-related metrics. Default is false.
|
||||
NoPrometheus bool `toml:"no_prometheus"`
|
||||
NoPrometheus bool `toml:"no_prometheus" json:"no_prometheus"`
|
||||
|
||||
// BlobConfig is config for layer blob management.
|
||||
BlobConfig `toml:"blob"`
|
||||
BlobConfig `toml:"blob" json:"blob"`
|
||||
|
||||
// DirectoryCacheConfig is config for directory-based cache.
|
||||
DirectoryCacheConfig `toml:"directory_cache"`
|
||||
DirectoryCacheConfig `toml:"directory_cache" json:"directory_cache"`
|
||||
|
||||
// FuseConfig is configurations for FUSE fs.
|
||||
FuseConfig `toml:"fuse"`
|
||||
FuseConfig `toml:"fuse" json:"fuse"`
|
||||
|
||||
// ResolveResultEntry is a deprecated field.
|
||||
ResolveResultEntry int `toml:"resolve_result_entry"` // deprecated
|
||||
ResolveResultEntry int `toml:"resolve_result_entry" json:"resolve_result_entry"` // deprecated
|
||||
}
|
||||
|
||||
// BlobConfig is configuration for the logic to fetching blobs.
|
||||
type BlobConfig struct {
|
||||
// ValidInterval specifies a duration (in seconds) during which the layer can be reused without
|
||||
// checking the connection to the registry. Default is 60.
|
||||
ValidInterval int64 `toml:"valid_interval"`
|
||||
ValidInterval int64 `toml:"valid_interval" json:"valid_interval"`
|
||||
|
||||
// CheckAlways overwrites ValidInterval to 0 if it's true. Default is false.
|
||||
CheckAlways bool `toml:"check_always"`
|
||||
CheckAlways bool `toml:"check_always" json:"check_always"`
|
||||
|
||||
// ChunkSize is the granularity (in bytes) at which background fetch and on-demand reads
|
||||
// are fetched from the remote registry. Default is 50000.
|
||||
ChunkSize int64 `toml:"chunk_size"`
|
||||
ChunkSize int64 `toml:"chunk_size" json:"chunk_size"`
|
||||
|
||||
// FetchTimeoutSec is a timeout duration (in seconds) for fetching chunks from the registry. Default is 300.
|
||||
FetchTimeoutSec int64 `toml:"fetching_timeout_sec"`
|
||||
FetchTimeoutSec int64 `toml:"fetching_timeout_sec" json:"fetching_tieout_sec"`
|
||||
|
||||
// ForceSingleRangeMode disables using of multiple ranges in a Range Request and always specifies one larger
|
||||
// region that covers them. Default is false.
|
||||
ForceSingleRangeMode bool `toml:"force_single_range_mode"`
|
||||
ForceSingleRangeMode bool `toml:"force_single_range_mode" json:"force_single_range_mode"`
|
||||
|
||||
// PrefetchChunkSize is the maximum bytes transferred per http GET from remote registry
|
||||
// during prefetch. It is recommended to have PrefetchChunkSize > ChunkSize.
|
||||
// If PrefetchChunkSize < ChunkSize prefetch bytes will be fetched as a single http GET,
|
||||
// else total GET requests for prefetch = ceil(PrefetchSize / PrefetchChunkSize).
|
||||
// Default is 0.
|
||||
PrefetchChunkSize int64 `toml:"prefetch_chunk_size"`
|
||||
PrefetchChunkSize int64 `toml:"prefetch_chunk_size" json:"prefetch_chunk_size"`
|
||||
|
||||
// MaxRetries is a max number of reries of a HTTP request. Default is 5.
|
||||
MaxRetries int `toml:"max_retries"`
|
||||
MaxRetries int `toml:"max_retries" json:"max_retries"`
|
||||
|
||||
// MinWaitMSec is minimal delay (in seconds) for the next retrying after a request failure. Default is 30.
|
||||
MinWaitMSec int `toml:"min_wait_msec"`
|
||||
MinWaitMSec int `toml:"min_wait_msec" json:"min_wait_msec"`
|
||||
|
||||
// MinWaitMSec is maximum delay (in seconds) for the next retrying after a request failure. Default is 30.
|
||||
MaxWaitMSec int `toml:"max_wait_msec"`
|
||||
MaxWaitMSec int `toml:"max_wait_msec" json:"max_wait_msec"`
|
||||
}
|
||||
|
||||
// DirectoryCacheConfig is configuration for the disk-based cache.
|
||||
type DirectoryCacheConfig struct {
|
||||
// MaxLRUCacheEntry is the number of entries of LRU cache to cache data on memory. Default is 10.
|
||||
MaxLRUCacheEntry int `toml:"max_lru_cache_entry"`
|
||||
MaxLRUCacheEntry int `toml:"max_lru_cache_entry" json:"max_lru_cache_entry"`
|
||||
|
||||
// MaxCacheFds is the number of entries of LRU cache to hold fds of files of cached contents. Default is 10.
|
||||
MaxCacheFds int `toml:"max_cache_fds"`
|
||||
MaxCacheFds int `toml:"max_cache_fds" json:"max_cache_fds"`
|
||||
|
||||
// SyncAdd being true means that each adding of data to the cache blocks until the data is fully written to the
|
||||
// cache directory. Default is false.
|
||||
SyncAdd bool `toml:"sync_add"`
|
||||
SyncAdd bool `toml:"sync_add" json:"sync_add"`
|
||||
|
||||
// Direct disables on-memory data cache. Default is true for saving memory usage.
|
||||
Direct bool `toml:"direct" default:"true"`
|
||||
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.
|
||||
type FuseConfig struct {
|
||||
// AttrTimeout defines overall timeout attribute for a file system in seconds.
|
||||
AttrTimeout int64 `toml:"attr_timeout"`
|
||||
AttrTimeout int64 `toml:"attr_timeout" json:"attr_timeout"`
|
||||
|
||||
// EntryTimeout defines TTL for directory, name lookup in seconds.
|
||||
EntryTimeout int64 `toml:"entry_timeout"`
|
||||
EntryTimeout int64 `toml:"entry_timeout" json:"entry_timeout"`
|
||||
|
||||
// PassThrough indicates whether to enable FUSE passthrough mode to improve local file read performance. Default is false.
|
||||
PassThrough bool `toml:"passthrough" default:"false" json:"passthrough"`
|
||||
|
||||
// MergeBufferSize is the size of the buffer to merge chunks (in bytes) for passthrough mode. Default is 400MB.
|
||||
MergeBufferSize int64 `toml:"merge_buffer_size" default:"419430400" json:"merge_buffer_size"`
|
||||
|
||||
// MergeWorkerCount is the number of workers to merge chunks for passthrough mode. Default is 10.
|
||||
MergeWorkerCount int `toml:"merge_worker_count" default:"10" json:"merge_worker_count"`
|
||||
}
|
||||
|
|
30
fs/fs.go
30
fs/fs.go
|
@ -72,6 +72,12 @@ const (
|
|||
)
|
||||
|
||||
var fusermountBin = []string{"fusermount", "fusermount3"}
|
||||
var (
|
||||
nsLock = sync.Mutex{}
|
||||
|
||||
ns *metrics.Namespace
|
||||
metricsCtr *layermetrics.Controller
|
||||
)
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
|
@ -133,12 +139,12 @@ func NewFilesystem(root string, cfg config.Config, opts ...Option) (_ snapshot.F
|
|||
maxConcurrency = defaultMaxConcurrency
|
||||
}
|
||||
|
||||
attrTimeout := time.Duration(cfg.FuseConfig.AttrTimeout) * time.Second
|
||||
attrTimeout := time.Duration(cfg.AttrTimeout) * time.Second
|
||||
if attrTimeout == 0 {
|
||||
attrTimeout = defaultFuseTimeout
|
||||
}
|
||||
|
||||
entryTimeout := time.Duration(cfg.FuseConfig.EntryTimeout) * time.Second
|
||||
entryTimeout := time.Duration(cfg.EntryTimeout) * time.Second
|
||||
if entryTimeout == 0 {
|
||||
entryTimeout = defaultFuseTimeout
|
||||
}
|
||||
|
@ -160,18 +166,20 @@ func NewFilesystem(root string, cfg config.Config, opts ...Option) (_ snapshot.F
|
|||
return nil, fmt.Errorf("failed to setup resolver: %w", err)
|
||||
}
|
||||
|
||||
var ns *metrics.Namespace
|
||||
if !cfg.NoPrometheus {
|
||||
nsLock.Lock()
|
||||
defer nsLock.Unlock()
|
||||
|
||||
if !cfg.NoPrometheus && ns == nil {
|
||||
ns = metrics.NewNamespace("stargz", "fs", nil)
|
||||
logLevel := log.DebugLevel
|
||||
if fsOpts.metricsLogLevel != nil {
|
||||
logLevel = *fsOpts.metricsLogLevel
|
||||
}
|
||||
commonmetrics.Register(logLevel) // Register common metrics. This will happen only once.
|
||||
metrics.Register(ns) // Register layer metrics.
|
||||
}
|
||||
c := layermetrics.NewLayerMetrics(ns)
|
||||
if ns != nil {
|
||||
metrics.Register(ns) // Register layer metrics.
|
||||
if metricsCtr == nil {
|
||||
metricsCtr = layermetrics.NewLayerMetrics(ns)
|
||||
}
|
||||
|
||||
return &filesystem{
|
||||
|
@ -185,7 +193,7 @@ func NewFilesystem(root string, cfg config.Config, opts ...Option) (_ snapshot.F
|
|||
backgroundTaskManager: tm,
|
||||
allowNoVerification: cfg.AllowNoVerification,
|
||||
disableVerification: cfg.DisableVerification,
|
||||
metricsController: c,
|
||||
metricsController: metricsCtr,
|
||||
attrTimeout: attrTimeout,
|
||||
entryTimeout: entryTimeout,
|
||||
}, nil
|
||||
|
@ -442,8 +450,10 @@ func (fs *filesystem) Unmount(ctx context.Context, mountpoint string) error {
|
|||
fs.layerMu.Unlock()
|
||||
return fmt.Errorf("specified path %q isn't a mountpoint", mountpoint)
|
||||
}
|
||||
delete(fs.layer, mountpoint) // unregisters the corresponding layer
|
||||
l.Done()
|
||||
delete(fs.layer, mountpoint) // unregisters the corresponding layer
|
||||
if err := l.Close(); err != nil { // Cleanup associated resources
|
||||
log.G(ctx).WithError(err).Warn("failed to release resources of the layer")
|
||||
}
|
||||
fs.layerMu.Unlock()
|
||||
fs.metricsController.Remove(mountpoint)
|
||||
|
||||
|
|
|
@ -91,4 +91,5 @@ func (l *breakableLayer) Refresh(ctx context.Context, hosts source.RegistryHosts
|
|||
}
|
||||
return nil
|
||||
}
|
||||
func (l *breakableLayer) Done() {}
|
||||
func (l *breakableLayer) Done() {}
|
||||
func (l *breakableLayer) Close() error { return nil }
|
||||
|
|
|
@ -59,6 +59,18 @@ const (
|
|||
memoryCacheType = "memory"
|
||||
)
|
||||
|
||||
// passThroughConfig contains configuration for FUSE passthrough mode
|
||||
type passThroughConfig struct {
|
||||
// enable indicates whether to enable FUSE passthrough mode
|
||||
enable bool
|
||||
|
||||
// mergeBufferSize is the size of the buffer to merge chunks (in bytes)
|
||||
mergeBufferSize int64
|
||||
|
||||
// mergeWorkerCount is the number of workers to merge chunks
|
||||
mergeWorkerCount int
|
||||
}
|
||||
|
||||
// Layer represents a layer.
|
||||
type Layer interface {
|
||||
// Info returns the information of this layer.
|
||||
|
@ -98,6 +110,10 @@ type Layer interface {
|
|||
// Done releases the reference to this layer. The resources related to this layer will be
|
||||
// discarded sooner or later. Queries after calling this function won't be serviced.
|
||||
Done()
|
||||
|
||||
// Close is the same as Done. But this evicts the resources related to this Layer immediately.
|
||||
// This can be used for cleaning up resources on unmount.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Info is the current status of a layer.
|
||||
|
@ -219,11 +235,12 @@ func newCache(root string, cacheType string, cfg config.Config) (cache.BlobCache
|
|||
return cache.NewDirectoryCache(
|
||||
cachePath,
|
||||
cache.DirectoryCacheConfig{
|
||||
SyncAdd: dcc.SyncAdd,
|
||||
DataCache: dCache,
|
||||
FdCache: fCache,
|
||||
BufPool: bufPool,
|
||||
Direct: dcc.Direct,
|
||||
SyncAdd: dcc.SyncAdd,
|
||||
DataCache: dCache,
|
||||
FdCache: fCache,
|
||||
BufPool: bufPool,
|
||||
Direct: dcc.Direct,
|
||||
FadvDontNeed: dcc.FadvDontNeed,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -249,7 +266,7 @@ func (r *Resolver) Resolve(ctx context.Context, hosts source.RegistryHosts, refs
|
|||
return &layerRef{l, done}, nil
|
||||
}
|
||||
// Cached layer is invalid
|
||||
done()
|
||||
done(true)
|
||||
r.layerCacheMu.Lock()
|
||||
r.layerCache.Remove(name)
|
||||
r.layerCacheMu.Unlock()
|
||||
|
@ -264,7 +281,7 @@ func (r *Resolver) Resolve(ctx context.Context, hosts source.RegistryHosts, refs
|
|||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
blobR.done()
|
||||
blobR.done(true)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -315,7 +332,11 @@ func (r *Resolver) Resolve(ctx context.Context, hosts source.RegistryHosts, refs
|
|||
}
|
||||
|
||||
// Combine layer information together and cache it.
|
||||
l := newLayer(r, desc, blobR, vr)
|
||||
l := newLayer(r, desc, blobR, vr, passThroughConfig{
|
||||
enable: r.config.PassThrough,
|
||||
mergeBufferSize: r.config.MergeBufferSize,
|
||||
mergeWorkerCount: r.config.MergeWorkerCount,
|
||||
})
|
||||
r.layerCacheMu.Lock()
|
||||
cachedL, done2, added := r.layerCache.Add(name, l)
|
||||
r.layerCacheMu.Unlock()
|
||||
|
@ -340,7 +361,7 @@ func (r *Resolver) resolveBlob(ctx context.Context, hosts source.RegistryHosts,
|
|||
return &blobRef{blob, done}, nil
|
||||
}
|
||||
// invalid blob. discard this.
|
||||
done()
|
||||
done(true)
|
||||
r.blobCacheMu.Lock()
|
||||
r.blobCache.Remove(name)
|
||||
r.blobCacheMu.Unlock()
|
||||
|
@ -375,6 +396,7 @@ func newLayer(
|
|||
desc ocispec.Descriptor,
|
||||
blob *blobRef,
|
||||
vr *reader.VerifiableReader,
|
||||
pth passThroughConfig,
|
||||
) *layer {
|
||||
return &layer{
|
||||
resolver: resolver,
|
||||
|
@ -382,6 +404,7 @@ func newLayer(
|
|||
blob: blob,
|
||||
verifiableReader: vr,
|
||||
prefetchWaiter: newWaiter(),
|
||||
passThrough: pth,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -402,6 +425,7 @@ type layer struct {
|
|||
|
||||
prefetchOnce sync.Once
|
||||
backgroundFetchOnce sync.Once
|
||||
passThrough passThroughConfig
|
||||
}
|
||||
|
||||
func (l *layer) Info() Info {
|
||||
|
@ -573,7 +597,12 @@ func (l *layer) backgroundFetch(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (l *layerRef) Done() {
|
||||
l.done()
|
||||
l.done(false) // leave chances to reuse this
|
||||
}
|
||||
|
||||
func (l *layerRef) Close() error {
|
||||
l.done(true) // evict this from the cache
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *layer) RootNode(baseInode uint32) (fusefs.InodeEmbedder, error) {
|
||||
|
@ -583,7 +612,7 @@ func (l *layer) RootNode(baseInode uint32) (fusefs.InodeEmbedder, error) {
|
|||
if l.r == nil {
|
||||
return nil, fmt.Errorf("layer hasn't been verified yet")
|
||||
}
|
||||
return newNode(l.desc.Digest, l.r, l.blob, baseInode, l.resolver.overlayOpaqueType)
|
||||
return newNode(l.desc.Digest, l.r, l.blob, baseInode, l.resolver.overlayOpaqueType, l.passThrough)
|
||||
}
|
||||
|
||||
func (l *layer) ReadAt(p []byte, offset int64, opts ...remote.Option) (int, error) {
|
||||
|
@ -597,7 +626,7 @@ func (l *layer) close() error {
|
|||
return nil
|
||||
}
|
||||
l.closed = true
|
||||
defer l.blob.done() // Close reader first, then close the blob
|
||||
defer l.blob.done(true) // Close reader first, then close the blob
|
||||
l.verifiableReader.Close()
|
||||
if l.r != nil {
|
||||
return l.r.Close()
|
||||
|
@ -617,7 +646,7 @@ func (l *layer) isClosed() bool {
|
|||
// to this blob will be discarded.
|
||||
type blobRef struct {
|
||||
remote.Blob
|
||||
done func()
|
||||
done func(bool)
|
||||
}
|
||||
|
||||
// layerRef is a reference to the layer in the cache. Calling `Done` or `done` decreases the
|
||||
|
@ -625,7 +654,7 @@ type blobRef struct {
|
|||
// cache, resources bound to this layer will be discarded.
|
||||
type layerRef struct {
|
||||
*layer
|
||||
done func()
|
||||
done func(bool)
|
||||
}
|
||||
|
||||
func newWaiter() *waiter {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -76,7 +76,7 @@ var opaqueXattrs = map[OverlayOpaqueType][]string{
|
|||
OverlayOpaqueUser: {"user.overlay.opaque"},
|
||||
}
|
||||
|
||||
func newNode(layerDgst digest.Digest, r reader.Reader, blob remote.Blob, baseInode uint32, opaque OverlayOpaqueType) (fusefs.InodeEmbedder, error) {
|
||||
func newNode(layerDgst digest.Digest, r reader.Reader, blob remote.Blob, baseInode uint32, opaque OverlayOpaqueType, pth passThroughConfig) (fusefs.InodeEmbedder, error) {
|
||||
rootID := r.Metadata().RootID()
|
||||
rootAttr, err := r.Metadata().GetAttr(rootID)
|
||||
if err != nil {
|
||||
|
@ -84,7 +84,7 @@ func newNode(layerDgst digest.Digest, r reader.Reader, blob remote.Blob, baseIno
|
|||
}
|
||||
opq, ok := opaqueXattrs[opaque]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Unknown overlay opaque type")
|
||||
return nil, fmt.Errorf("unknown overlay opaque type")
|
||||
}
|
||||
ffs := &fs{
|
||||
r: r,
|
||||
|
@ -92,6 +92,7 @@ func newNode(layerDgst digest.Digest, r reader.Reader, blob remote.Blob, baseIno
|
|||
baseInode: baseInode,
|
||||
rootID: rootID,
|
||||
opaqueXattrs: opq,
|
||||
passThrough: pth,
|
||||
}
|
||||
ffs.s = ffs.newState(layerDgst, blob)
|
||||
return &node{
|
||||
|
@ -109,6 +110,7 @@ type fs struct {
|
|||
baseInode uint32
|
||||
rootID uint32
|
||||
opaqueXattrs []string
|
||||
passThrough passThroughConfig
|
||||
}
|
||||
|
||||
func (fs *fs) inodeOfState() uint64 {
|
||||
|
@ -344,10 +346,26 @@ func (n *node) Open(ctx context.Context, flags uint32) (fh fusefs.FileHandle, fu
|
|||
n.fs.s.report(fmt.Errorf("node.Open: %v", err))
|
||||
return nil, 0, syscall.EIO
|
||||
}
|
||||
return &file{
|
||||
|
||||
f := &file{
|
||||
n: n,
|
||||
ra: ra,
|
||||
}, fuse.FOPEN_KEEP_CACHE, 0
|
||||
fd: -1,
|
||||
}
|
||||
|
||||
if n.fs.passThrough.enable {
|
||||
if getter, ok := ra.(reader.PassthroughFdGetter); ok {
|
||||
fd, err := getter.GetPassthroughFd(n.fs.passThrough.mergeBufferSize, n.fs.passThrough.mergeWorkerCount)
|
||||
if err != nil {
|
||||
n.fs.s.report(fmt.Errorf("passThrough model failed due to node.Open: %v", err))
|
||||
n.fs.passThrough.enable = false
|
||||
} else {
|
||||
f.InitFd(int(fd))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return f, fuse.FOPEN_KEEP_CACHE, 0
|
||||
}
|
||||
|
||||
var _ = (fusefs.NodeGetattrer)((*node)(nil))
|
||||
|
@ -424,6 +442,7 @@ func (n *node) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
|
|||
type file struct {
|
||||
n *node
|
||||
ra io.ReaderAt
|
||||
fd int
|
||||
}
|
||||
|
||||
var _ = (fusefs.FileReader)((*file)(nil))
|
||||
|
@ -451,6 +470,20 @@ func (f *file) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno {
|
|||
return 0
|
||||
}
|
||||
|
||||
// Implement PassthroughFd to enable go-fuse passthrough
|
||||
var _ = (fusefs.FilePassthroughFder)((*file)(nil))
|
||||
|
||||
func (f *file) PassthroughFd() (int, bool) {
|
||||
if f.fd <= 0 {
|
||||
return -1, false
|
||||
}
|
||||
return f.fd, true
|
||||
}
|
||||
|
||||
func (f *file) InitFd(fd int) {
|
||||
f.fd = fd
|
||||
}
|
||||
|
||||
// whiteout is a whiteout abstraction compliant to overlayfs.
|
||||
type whiteout struct {
|
||||
fusefs.Inode
|
||||
|
@ -799,7 +832,7 @@ func defaultStatfs(stat *fuse.StatfsOut) {
|
|||
stat.Files = 0 // dummy
|
||||
stat.Ffree = 0
|
||||
stat.Bsize = blockSize
|
||||
stat.NameLen = 1<<32 - 1
|
||||
stat.NameLen = 255 // Standard max filename length for most filesystems (ext4, etc.) for compatibility
|
||||
stat.Frsize = blockSize
|
||||
stat.Padding = 0
|
||||
stat.Spare = [6]uint32{}
|
||||
|
|
|
@ -39,7 +39,6 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/pkg/reference"
|
||||
|
@ -71,18 +70,86 @@ var srcCompressions = map[string]tutil.CompressionFactory{
|
|||
"externaltoc-gzip-bestspeed": tutil.ExternalTOCGzipCompressionWithLevel(gzip.BestSpeed),
|
||||
}
|
||||
|
||||
func TestSuiteLayer(t *testing.T, store metadata.Store) {
|
||||
testPrefetch(t, store)
|
||||
testNodeRead(t, store)
|
||||
testNodes(t, store)
|
||||
type layerConfig struct {
|
||||
name string
|
||||
passThroughConfig passThroughConfig
|
||||
}
|
||||
|
||||
// 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",
|
||||
passThroughConfig: passThroughConfig{
|
||||
enable: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "passthrough",
|
||||
passThroughConfig: passThroughConfig{
|
||||
enable: true,
|
||||
mergeBufferSize: 419430400,
|
||||
mergeWorkerCount: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "passthorough without concorrency",
|
||||
passThroughConfig: passThroughConfig{
|
||||
enable: true,
|
||||
mergeBufferSize: 419430400,
|
||||
mergeWorkerCount: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "passthorough with small buffer",
|
||||
passThroughConfig: passThroughConfig{
|
||||
enable: true,
|
||||
mergeBufferSize: 1,
|
||||
mergeWorkerCount: 10,
|
||||
},
|
||||
},
|
||||
} {
|
||||
testPrefetch(t, store, lc)
|
||||
testNodeRead(t, store, lc)
|
||||
testNodes(t, store, lc)
|
||||
}
|
||||
}
|
||||
|
||||
var testStateLayerDigest = digest.FromString("dummy")
|
||||
|
||||
func testPrefetch(t *testing.T, factory metadata.Store) {
|
||||
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")
|
||||
}
|
||||
|
@ -102,7 +169,7 @@ func testPrefetch(t *testing.T, factory metadata.Store) {
|
|||
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
|
||||
}{
|
||||
{
|
||||
|
@ -180,7 +247,7 @@ func testPrefetch(t *testing.T, factory metadata.Store) {
|
|||
for _, tt := range tests {
|
||||
for srcCompressionName, srcCompression := range srcCompressions {
|
||||
cl := srcCompression()
|
||||
t.Run("testPrefetch-"+tt.name+"-"+srcCompressionName, func(t *testing.T) {
|
||||
t.Run("testPrefetch-"+tt.name+"-"+srcCompressionName+"-"+lc.name, func(t *TestRunner) {
|
||||
chunkSize := sampleChunkSize
|
||||
if tt.chunkSize > 0 {
|
||||
chunkSize = tt.chunkSize
|
||||
|
@ -216,8 +283,9 @@ func testPrefetch(t *testing.T, factory metadata.Store) {
|
|||
backgroundTaskManager: task.NewBackgroundTaskManager(10, 5*time.Second),
|
||||
},
|
||||
ocispec.Descriptor{Digest: testStateLayerDigest},
|
||||
&blobRef{blob, func() {}},
|
||||
&blobRef{blob, func(bool) {}},
|
||||
vr,
|
||||
lc.passThroughConfig,
|
||||
)
|
||||
if err := l.Verify(dgst); err != nil {
|
||||
t.Errorf("failed to verify reader: %v", err)
|
||||
|
@ -306,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,
|
||||
|
@ -314,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
|
||||
|
@ -379,7 +447,7 @@ const (
|
|||
lastChunkOffset1 = sampleChunkSize * (int64(len(sampleData1)) / sampleChunkSize)
|
||||
)
|
||||
|
||||
func testNodeRead(t *testing.T, factory metadata.Store) {
|
||||
func testNodeRead(t *TestRunner, factory metadata.Store, lc layerConfig) {
|
||||
sizeCond := map[string]int64{
|
||||
"single_chunk": sampleChunkSize - sampleMiddleOffset,
|
||||
"multi_chunks": sampleChunkSize + sampleMiddleOffset,
|
||||
|
@ -404,7 +472,7 @@ func testNodeRead(t *testing.T, factory metadata.Store) {
|
|||
for fn, filesize := range fileSizeCond {
|
||||
for _, srcCompression := range srcCompressions {
|
||||
cl := srcCompression()
|
||||
t.Run(fmt.Sprintf("reading_%s_%s_%s_%s", sn, in, bo, fn), 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")
|
||||
}
|
||||
|
@ -428,7 +496,7 @@ func testNodeRead(t *testing.T, factory metadata.Store) {
|
|||
}
|
||||
|
||||
// data we get from the file node.
|
||||
f, closeFn := makeNodeReader(t, []byte(sampleData1)[:filesize], sampleChunkSize, factory, cl)
|
||||
f, closeFn := makeNodeReader(t, []byte(sampleData1)[:filesize], sampleChunkSize, factory, cl, lc)
|
||||
defer closeFn()
|
||||
tmpbuf := make([]byte, size) // fuse library can request bigger than remain
|
||||
rr, errno := f.Read(context.Background(), tmpbuf, offset)
|
||||
|
@ -459,7 +527,7 @@ func testNodeRead(t *testing.T, factory metadata.Store) {
|
|||
}
|
||||
}
|
||||
|
||||
func makeNodeReader(t *testing.T, contents []byte, chunkSize int, factory metadata.Store, cl tutil.Compression) (_ *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))},
|
||||
|
@ -472,7 +540,7 @@ func makeNodeReader(t *testing.T, contents []byte, chunkSize int, factory metada
|
|||
if err != nil {
|
||||
t.Fatalf("failed to create reader: %v", err)
|
||||
}
|
||||
rootNode := getRootNode(t, r, OverlayOpaqueAll, tocDgst, cache.NewMemoryCache())
|
||||
rootNode := getRootNode(t, r, OverlayOpaqueAll, tocDgst, cache.NewMemoryCache(), lc)
|
||||
var eo fuse.EntryOut
|
||||
inode, errno := rootNode.Lookup(context.Background(), testName, &eo)
|
||||
if errno != 0 {
|
||||
|
@ -487,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) {
|
||||
func testNodes(t *TestRunner, factory metadata.Store, lc layerConfig) {
|
||||
for _, o := range []OverlayOpaqueType{OverlayOpaqueAll, OverlayOpaqueTrusted, OverlayOpaqueUser} {
|
||||
testNodesWithOpaque(t, factory, o)
|
||||
testNodesWithOpaque(t, factory, o, lc)
|
||||
}
|
||||
}
|
||||
|
||||
func testNodesWithOpaque(t *testing.T, factory metadata.Store, opaque OverlayOpaqueType) {
|
||||
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)
|
||||
}
|
||||
|
@ -702,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, func(t *testing.T) {
|
||||
t.Run(tt.name+"-"+lc.name, func(t *TestRunner) {
|
||||
opts := []tutil.BuildEStargzOption{
|
||||
tutil.WithEStargzOptions(estargz.WithCompression(cl)),
|
||||
}
|
||||
|
@ -724,7 +796,7 @@ func testNodesWithOpaque(t *testing.T, factory metadata.Store, opaque OverlayOpa
|
|||
}
|
||||
defer r.Close()
|
||||
mcache := cache.NewMemoryCache()
|
||||
rootNode := getRootNode(t, r, opaque, tocDgst, mcache)
|
||||
rootNode := getRootNode(t, r, opaque, tocDgst, mcache, lc)
|
||||
for _, want := range tt.want {
|
||||
want(t, rootNode, mcache, testR)
|
||||
}
|
||||
|
@ -733,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) *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)
|
||||
|
@ -742,7 +814,7 @@ func getRootNode(t *testing.T, r metadata.Reader, opaque OverlayOpaqueType, tocD
|
|||
if err != nil {
|
||||
t.Fatalf("failed to verify reader: %v", err)
|
||||
}
|
||||
rootNode, err := newNode(testStateLayerDigest, rr, &testBlobState{10, 5}, 100, opaque)
|
||||
rootNode, err := newNode(testStateLayerDigest, rr, &testBlobState{10, 5}, 100, opaque, lc.passThroughConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get root node: %v", err)
|
||||
}
|
||||
|
@ -767,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)
|
||||
}
|
||||
|
@ -778,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)
|
||||
|
@ -807,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)
|
||||
|
@ -816,14 +888,14 @@ func hasSize(name string, size int) check {
|
|||
if errno := n.Operations().(fusefs.NodeGetattrer).Getattr(context.Background(), nil, &ao); errno != 0 {
|
||||
t.Fatalf("failed to get attributes of node %q: %v", name, errno)
|
||||
}
|
||||
if ao.Attr.Size != uint64(size) {
|
||||
t.Fatalf("got size = %d, want %d", ao.Attr.Size, size)
|
||||
if ao.Size != uint64(size) {
|
||||
t.Fatalf("got size = %d, want %d", ao.Size, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -842,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)
|
||||
|
@ -878,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)
|
||||
|
@ -919,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 {
|
||||
|
@ -932,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())
|
||||
|
@ -1030,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.
|
||||
|
@ -1106,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))
|
||||
|
@ -1128,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) {
|
||||
|
@ -1150,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)
|
||||
|
|
|
@ -27,10 +27,12 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -38,7 +40,6 @@ import (
|
|||
"github.com/containerd/stargz-snapshotter/estargz"
|
||||
commonmetrics "github.com/containerd/stargz-snapshotter/fs/metrics/common"
|
||||
"github.com/containerd/stargz-snapshotter/metadata"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
@ -53,6 +54,10 @@ type Reader interface {
|
|||
LastOnDemandReadTime() time.Time
|
||||
}
|
||||
|
||||
type PassthroughFdGetter interface {
|
||||
GetPassthroughFd(mergeBufferSize int64, mergeWorkerCount int) (uintptr, error)
|
||||
}
|
||||
|
||||
// VerifiableReader produces a Reader with a given verifier.
|
||||
type VerifiableReader struct {
|
||||
r *reader
|
||||
|
@ -386,20 +391,21 @@ func (gr *reader) OpenFile(id uint32) (io.ReaderAt, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (gr *reader) Close() (retErr error) {
|
||||
func (gr *reader) Close() error {
|
||||
gr.closedMu.Lock()
|
||||
defer gr.closedMu.Unlock()
|
||||
if gr.closed {
|
||||
return nil
|
||||
}
|
||||
gr.closed = true
|
||||
var errs []error
|
||||
if err := gr.cache.Close(); err != nil {
|
||||
retErr = multierror.Append(retErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := gr.r.Close(); err != nil {
|
||||
retErr = multierror.Append(retErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (gr *reader) isClosed() bool {
|
||||
|
@ -490,18 +496,316 @@ func (sf *file) ReadAt(p []byte, offset int64) (int, error) {
|
|||
return nr, nil
|
||||
}
|
||||
|
||||
func (gr *reader) verifyAndCache(entryID uint32, ip []byte, chunkDigestStr string, cacheID string) error {
|
||||
// We can end up doing on demand registry fetch when aligning the chunk
|
||||
commonmetrics.IncOperationCount(commonmetrics.OnDemandRemoteRegistryFetchCount, gr.layerSha) // increment the number of on demand file fetches from remote registry
|
||||
commonmetrics.AddBytesCount(commonmetrics.OnDemandBytesFetched, gr.layerSha, int64(len(ip))) // record total bytes fetched
|
||||
gr.setLastReadTime(time.Now())
|
||||
type chunkData struct {
|
||||
offset int64
|
||||
size int64
|
||||
digestStr string
|
||||
bufferPos int64
|
||||
}
|
||||
|
||||
// Verify this chunk
|
||||
func (sf *file) GetPassthroughFd(mergeBufferSize int64, mergeWorkerCount int) (uintptr, error) {
|
||||
var (
|
||||
offset int64
|
||||
firstChunkOffset int64
|
||||
totalSize int64
|
||||
hasLargeChunk bool
|
||||
)
|
||||
|
||||
var chunks []chunkData
|
||||
for {
|
||||
chunkOffset, chunkSize, digestStr, ok := sf.fr.ChunkEntryForOffset(offset)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
// Check if any chunk size exceeds merge buffer size to avoid bounds out of range
|
||||
if chunkSize > mergeBufferSize {
|
||||
hasLargeChunk = true
|
||||
}
|
||||
chunks = append(chunks, chunkData{
|
||||
offset: chunkOffset,
|
||||
size: chunkSize,
|
||||
digestStr: digestStr,
|
||||
})
|
||||
totalSize += chunkSize
|
||||
offset = chunkOffset + chunkSize
|
||||
}
|
||||
|
||||
id := genID(sf.id, firstChunkOffset, totalSize)
|
||||
|
||||
// cache.PassThrough() is necessary to take over files
|
||||
r, err := sf.gr.cache.Get(id, cache.PassThrough())
|
||||
if err != nil {
|
||||
if hasLargeChunk {
|
||||
if err := sf.prefetchEntireFileSequential(id); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
if err := sf.prefetchEntireFile(id, chunks, totalSize, mergeBufferSize, mergeWorkerCount); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// just retry once to avoid exception stuck
|
||||
r, err = sf.gr.cache.Get(id, cache.PassThrough())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
readerAt := r.GetReaderAt()
|
||||
file, ok := readerAt.(*os.File)
|
||||
if !ok {
|
||||
r.Close()
|
||||
return 0, fmt.Errorf("the cached ReaderAt is not of type *os.File, fd obtain failed")
|
||||
}
|
||||
|
||||
fd := file.Fd()
|
||||
r.Close()
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
// prefetchEntireFileSequential uses the legacy sequential approach for processing chunks
|
||||
// when chunk size exceeds merge buffer size to avoid slice bounds out of range panic
|
||||
func (sf *file) prefetchEntireFileSequential(entireCacheID string) error {
|
||||
w, err := sf.gr.cache.Add(entireCacheID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create cache writer: %w", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
var offset int64
|
||||
|
||||
for {
|
||||
chunkOffset, chunkSize, chunkDigestStr, ok := sf.fr.ChunkEntryForOffset(offset)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
id := genID(sf.id, chunkOffset, chunkSize)
|
||||
b := sf.gr.bufPool.Get().(*bytes.Buffer)
|
||||
b.Reset()
|
||||
b.Grow(int(chunkSize))
|
||||
ip := b.Bytes()[:chunkSize]
|
||||
|
||||
if r, err := sf.gr.cache.Get(id); err == nil {
|
||||
n, err := r.ReadAt(ip, 0)
|
||||
if (err == nil || err == io.EOF) && int64(n) == chunkSize {
|
||||
if _, err := w.Write(ip[:n]); err != nil {
|
||||
r.Close()
|
||||
sf.gr.putBuffer(b)
|
||||
w.Abort()
|
||||
return fmt.Errorf("failed to write cached data: %w", err)
|
||||
}
|
||||
offset = chunkOffset + int64(n)
|
||||
r.Close()
|
||||
sf.gr.putBuffer(b)
|
||||
continue
|
||||
}
|
||||
r.Close()
|
||||
}
|
||||
|
||||
if _, err := sf.fr.ReadAt(ip, chunkOffset); err != nil && err != io.EOF {
|
||||
sf.gr.putBuffer(b)
|
||||
w.Abort()
|
||||
return fmt.Errorf("failed to read data: %w", err)
|
||||
}
|
||||
if err := sf.gr.verifyOneChunk(sf.id, ip, chunkDigestStr); err != nil {
|
||||
sf.gr.putBuffer(b)
|
||||
w.Abort()
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(ip); err != nil {
|
||||
sf.gr.putBuffer(b)
|
||||
w.Abort()
|
||||
return fmt.Errorf("failed to write fetched data: %w", err)
|
||||
}
|
||||
offset = chunkOffset + chunkSize
|
||||
sf.gr.putBuffer(b)
|
||||
}
|
||||
|
||||
return w.Commit()
|
||||
}
|
||||
|
||||
type batchWorkerArgs struct {
|
||||
workerID int
|
||||
chunks []chunkData
|
||||
buffer []byte
|
||||
workerCount int
|
||||
readInfos []chunkReadInfo
|
||||
}
|
||||
|
||||
func (sf *file) prefetchEntireFile(entireCacheID string, chunks []chunkData, totalSize int64, bufferSize int64, workerCount int) error {
|
||||
|
||||
w, err := sf.gr.cache.Add(entireCacheID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create cache writer: %w", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
batchCount := (totalSize + bufferSize - 1) / bufferSize
|
||||
|
||||
for batchIdx := int64(0); batchIdx < batchCount; batchIdx++ {
|
||||
batchStart := batchIdx * bufferSize
|
||||
batchEnd := (batchIdx + 1) * bufferSize
|
||||
if batchEnd > totalSize {
|
||||
batchEnd = totalSize
|
||||
}
|
||||
|
||||
var batchChunks []chunkData
|
||||
var batchOffset int64
|
||||
for i := range chunks {
|
||||
chunkStart := chunks[i].offset
|
||||
chunkEnd := chunkStart + chunks[i].size
|
||||
|
||||
if chunkEnd <= batchStart {
|
||||
continue
|
||||
}
|
||||
if chunkStart >= batchEnd {
|
||||
break
|
||||
}
|
||||
|
||||
chunks[i].bufferPos = batchOffset
|
||||
batchOffset += chunks[i].size
|
||||
batchChunks = append(batchChunks, chunks[i])
|
||||
}
|
||||
|
||||
batchSize := batchEnd - batchStart
|
||||
buffer := make([]byte, batchSize)
|
||||
|
||||
eg := errgroup.Group{}
|
||||
allReadInfos := make([][]chunkReadInfo, workerCount)
|
||||
|
||||
for i := 0; i < workerCount && i < len(batchChunks); i++ {
|
||||
workerID := i
|
||||
args := &batchWorkerArgs{
|
||||
workerID: workerID,
|
||||
chunks: batchChunks,
|
||||
buffer: buffer,
|
||||
workerCount: workerCount,
|
||||
}
|
||||
eg.Go(func() error {
|
||||
err := sf.processBatchChunks(args)
|
||||
if err == nil && len(args.readInfos) > 0 {
|
||||
allReadInfos[args.workerID] = args.readInfos
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
w.Abort()
|
||||
return err
|
||||
}
|
||||
|
||||
var mergedReadInfos []chunkReadInfo
|
||||
for _, infos := range allReadInfos {
|
||||
mergedReadInfos = append(mergedReadInfos, infos...)
|
||||
}
|
||||
|
||||
if err := sf.checkHoles(mergedReadInfos, batchSize); err != nil {
|
||||
w.Abort()
|
||||
return fmt.Errorf("hole check failed: %w", err)
|
||||
}
|
||||
|
||||
n, err := w.Write(buffer)
|
||||
if err != nil {
|
||||
w.Abort()
|
||||
return fmt.Errorf("failed to write batch data: %w", err)
|
||||
}
|
||||
|
||||
if int64(n) != batchSize {
|
||||
w.Abort()
|
||||
return fmt.Errorf("incomplete write: expected %d bytes, wrote %d bytes", batchSize, n)
|
||||
}
|
||||
}
|
||||
|
||||
return w.Commit()
|
||||
}
|
||||
|
||||
type chunkReadInfo struct {
|
||||
offset int64
|
||||
size int64
|
||||
}
|
||||
|
||||
func (sf *file) checkHoles(readInfos []chunkReadInfo, totalSize int64) error {
|
||||
if len(readInfos) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sort.Slice(readInfos, func(i, j int) bool {
|
||||
return readInfos[i].offset < readInfos[j].offset
|
||||
})
|
||||
|
||||
end := readInfos[0].offset
|
||||
for _, info := range readInfos {
|
||||
if info.offset < end {
|
||||
return fmt.Errorf("overlapping read detected: previous end %d, current start %d", end, info.offset)
|
||||
} else if info.offset > end {
|
||||
return fmt.Errorf("hole detected in read: previous end %d, current start %d", end, info.offset)
|
||||
}
|
||||
end = info.offset + info.size
|
||||
}
|
||||
|
||||
if end != totalSize {
|
||||
return fmt.Errorf("incomplete read: expected total size %d, actual end %d", totalSize, end)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sf *file) processBatchChunks(args *batchWorkerArgs) error {
|
||||
var readInfos []chunkReadInfo
|
||||
|
||||
for chunkIdx := args.workerID; chunkIdx < len(args.chunks); chunkIdx += args.workerCount {
|
||||
chunk := args.chunks[chunkIdx]
|
||||
bufStart := args.buffer[chunk.bufferPos : chunk.bufferPos+chunk.size]
|
||||
|
||||
id := genID(sf.id, chunk.offset, chunk.size)
|
||||
if r, err := sf.gr.cache.Get(id); err == nil {
|
||||
n, err := r.ReadAt(bufStart, 0)
|
||||
r.Close()
|
||||
if err == nil || err == io.EOF {
|
||||
if int64(n) == chunk.size {
|
||||
readInfos = append(readInfos, chunkReadInfo{
|
||||
offset: chunk.bufferPos,
|
||||
size: int64(n),
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n, err := sf.fr.ReadAt(bufStart, chunk.offset)
|
||||
if err != nil && err != io.EOF {
|
||||
return fmt.Errorf("failed to read data at offset %d: %w", chunk.offset, err)
|
||||
}
|
||||
|
||||
readInfos = append(readInfos, chunkReadInfo{
|
||||
offset: chunk.bufferPos,
|
||||
size: int64(n),
|
||||
})
|
||||
|
||||
if err := sf.gr.verifyOneChunk(sf.id, bufStart, chunk.digestStr); err != nil {
|
||||
return fmt.Errorf("chunk verification failed at offset %d: %w", chunk.offset, err)
|
||||
}
|
||||
}
|
||||
|
||||
args.readInfos = readInfos
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gr *reader) verifyOneChunk(entryID uint32, ip []byte, chunkDigestStr string) error {
|
||||
// We can end up doing on demand registry fetch when aligning the chunk
|
||||
commonmetrics.IncOperationCount(commonmetrics.OnDemandRemoteRegistryFetchCount, gr.layerSha)
|
||||
commonmetrics.AddBytesCount(commonmetrics.OnDemandBytesFetched, gr.layerSha, int64(len(ip)))
|
||||
gr.setLastReadTime(time.Now())
|
||||
if err := gr.verifyChunk(entryID, ip, chunkDigestStr); err != nil {
|
||||
return fmt.Errorf("invalid chunk: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cache this chunk
|
||||
func (gr *reader) cacheData(ip []byte, cacheID string) {
|
||||
if w, err := gr.cache.Add(cacheID); err == nil {
|
||||
if cn, err := w.Write(ip); err != nil || cn != len(ip) {
|
||||
w.Abort()
|
||||
|
@ -510,7 +814,13 @@ func (gr *reader) verifyAndCache(entryID uint32, ip []byte, chunkDigestStr strin
|
|||
}
|
||||
w.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (gr *reader) verifyAndCache(entryID uint32, ip []byte, chunkDigestStr string, cacheID string) error {
|
||||
if err := gr.verifyOneChunk(entryID, ip, chunkDigestStr); err != nil {
|
||||
return err
|
||||
}
|
||||
gr.cacheData(ip, cacheID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/stargz-snapshotter/cache"
|
||||
|
@ -59,14 +58,47 @@ var srcCompressions = map[string]tutil.CompressionFactory{
|
|||
"externaltoc-gzip-bestspeed": tutil.ExternalTOCGzipCompressionWithLevel(gzip.BestSpeed),
|
||||
}
|
||||
|
||||
func TestSuiteReader(t *testing.T, store metadata.Store) {
|
||||
// MockReadAtOutput defines the default output size for mocked read operations
|
||||
var (
|
||||
MockReadAtOutput = 4194304
|
||||
)
|
||||
|
||||
// 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)
|
||||
testPreReader(t, 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,
|
||||
|
@ -103,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")
|
||||
}
|
||||
|
@ -193,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 {
|
||||
|
@ -205,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) {
|
||||
|
@ -219,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)),
|
||||
|
@ -259,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} {
|
||||
|
@ -267,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"),
|
||||
|
@ -477,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{
|
||||
|
@ -589,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
|
||||
|
@ -660,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)),
|
||||
}
|
||||
|
@ -699,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
|
||||
|
@ -709,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)
|
||||
|
@ -744,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)
|
||||
|
@ -819,3 +855,183 @@ func (b longBytesView) String() string {
|
|||
}
|
||||
return string(b[:50]) + "...(omit)..." + string(b[len(b)-50:])
|
||||
}
|
||||
|
||||
func makeMockFile(id uint32) *file {
|
||||
mockCache := &mockCache{
|
||||
getError: fmt.Errorf("mock cache get error"),
|
||||
}
|
||||
|
||||
mockFile := &mockFile{}
|
||||
|
||||
gr := &reader{
|
||||
cache: mockCache,
|
||||
}
|
||||
|
||||
return &file{
|
||||
id: id,
|
||||
fr: mockFile,
|
||||
gr: gr,
|
||||
}
|
||||
}
|
||||
|
||||
type mockCache struct {
|
||||
getError error
|
||||
}
|
||||
|
||||
func (c *mockCache) Add(key string, opts ...cache.Option) (cache.Writer, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (c *mockCache) Get(key string, opts ...cache.Option) (cache.Reader, error) {
|
||||
return nil, c.getError
|
||||
}
|
||||
|
||||
func (c *mockCache) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockFile struct{}
|
||||
|
||||
func (f *mockFile) ChunkEntryForOffset(offset int64) (off int64, size int64, dgst string, ok bool) {
|
||||
return 0, 0, "", true
|
||||
}
|
||||
|
||||
func (f *mockFile) ReadAt(p []byte, offset int64) (int, error) {
|
||||
return MockReadAtOutput, nil
|
||||
}
|
||||
|
||||
func testProcessBatchChunks(t *TestRunner) {
|
||||
type testCase struct {
|
||||
name string
|
||||
setupMock func()
|
||||
createChunks func(chunkSize int64, totalChunks int) []chunkData
|
||||
expectErrorInHoles bool
|
||||
}
|
||||
|
||||
runTest := func(t TestingT, tc testCase) {
|
||||
if tc.setupMock != nil {
|
||||
tc.setupMock()
|
||||
}
|
||||
|
||||
sf := makeMockFile(1)
|
||||
|
||||
const (
|
||||
bufferSize int64 = 400 * 1024 * 1024
|
||||
chunkSize int64 = 4 * 1024 * 1024
|
||||
workerCount int = 10
|
||||
totalChunks int = 100
|
||||
)
|
||||
|
||||
chunks := tc.createChunks(chunkSize, totalChunks)
|
||||
buffer := make([]byte, bufferSize)
|
||||
allReadInfos := make([][]chunkReadInfo, workerCount)
|
||||
eg := errgroup.Group{}
|
||||
|
||||
for i := 0; i < workerCount && i < len(chunks); i++ {
|
||||
workerID := i
|
||||
args := &batchWorkerArgs{
|
||||
workerID: workerID,
|
||||
chunks: chunks,
|
||||
buffer: buffer,
|
||||
workerCount: workerCount,
|
||||
}
|
||||
eg.Go(func() error {
|
||||
err := sf.processBatchChunks(args)
|
||||
if err == nil && len(args.readInfos) > 0 {
|
||||
allReadInfos[args.workerID] = args.readInfos
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
t.Fatalf("processBatchChunks failed: %v", err)
|
||||
}
|
||||
|
||||
var mergedReadInfos []chunkReadInfo
|
||||
for _, infos := range allReadInfos {
|
||||
mergedReadInfos = append(mergedReadInfos, infos...)
|
||||
}
|
||||
|
||||
err := sf.checkHoles(mergedReadInfos, bufferSize)
|
||||
if tc.expectErrorInHoles {
|
||||
if err == nil {
|
||||
t.Fatalf("checkHoles should have detected issues but didn't")
|
||||
}
|
||||
t.Logf("Expected error detected: %v", err)
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("checkHoles failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createNormalChunks := func(chunkSize int64, totalChunks int) []chunkData {
|
||||
var chunks []chunkData
|
||||
for i := 0; i < totalChunks; i++ {
|
||||
chunks = append(chunks, chunkData{
|
||||
offset: int64(i) * chunkSize,
|
||||
size: chunkSize,
|
||||
digestStr: fmt.Sprintf("sha256:%d", i),
|
||||
bufferPos: int64(i) * chunkSize,
|
||||
})
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
createOverlappingChunks := func(chunkSize int64, totalChunks int) []chunkData {
|
||||
chunks := createNormalChunks(chunkSize, totalChunks)
|
||||
|
||||
for i := 0; i < totalChunks; i++ {
|
||||
if i > 0 && i%10 == 0 {
|
||||
chunks = append(chunks, chunkData{
|
||||
offset: int64(i)*chunkSize - chunkSize/2,
|
||||
size: chunkSize,
|
||||
digestStr: fmt.Sprintf("sha256:overlap-%d", i),
|
||||
bufferPos: int64(i) * chunkSize,
|
||||
})
|
||||
|
||||
if i < totalChunks-1 {
|
||||
chunks = append(chunks, chunkData{
|
||||
offset: int64(i+1)*chunkSize + chunkSize/2,
|
||||
size: chunkSize,
|
||||
digestStr: fmt.Sprintf("sha256:gap-%d", i),
|
||||
bufferPos: int64(i+2) * chunkSize,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "test_process_batch_chunks_and_check_holes",
|
||||
createChunks: createNormalChunks,
|
||||
expectErrorInHoles: false,
|
||||
},
|
||||
{
|
||||
name: "test_process_batch_chunks_with_holes",
|
||||
setupMock: func() {
|
||||
originalMockReadAtOutput := MockReadAtOutput
|
||||
MockReadAtOutput = MockReadAtOutput / 2
|
||||
t.Cleanup(func() {
|
||||
MockReadAtOutput = originalMockReadAtOutput
|
||||
})
|
||||
},
|
||||
createChunks: createNormalChunks,
|
||||
expectErrorInHoles: true,
|
||||
},
|
||||
{
|
||||
name: "test_process_batch_chunks_with_overlapping",
|
||||
createChunks: createOverlappingChunks,
|
||||
expectErrorInHoles: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *TestRunner) {
|
||||
runTest(t, tc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ func (b *blob) Refresh(ctx context.Context, hosts source.RegistryHosts, refspec
|
|||
return err
|
||||
}
|
||||
if newSize != b.size {
|
||||
return fmt.Errorf("Invalid size of new blob %d; want %d", newSize, b.size)
|
||||
return fmt.Errorf("invalid size of new blob %d; want %d", newSize, b.size)
|
||||
}
|
||||
|
||||
// update the blob's fetcher with new one
|
||||
|
@ -259,13 +259,23 @@ func (b *blob) ReadAt(p []byte, offset int64, opts ...Option) (int, error) {
|
|||
o(&readAtOpts)
|
||||
}
|
||||
|
||||
// Fetcher can be suddenly updated so we take and use the snapshot of it for
|
||||
// consistency.
|
||||
b.fetcherMu.Lock()
|
||||
fr := b.fetcher
|
||||
b.fetcherMu.Unlock()
|
||||
fr := b.getFetcher()
|
||||
|
||||
b.walkChunks(allRegion, func(chunk region) error {
|
||||
if err := b.prepareChunksForRead(allRegion, offset, p, fr, allData, &readAtOpts); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Read required data
|
||||
if err := b.fetchRange(allData, &readAtOpts); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return b.adjustBufferSize(p, offset), nil
|
||||
}
|
||||
|
||||
// prepareChunksForRead prepares chunks for reading by checking cache and setting up writers
|
||||
func (b *blob) prepareChunksForRead(allRegion region, offset int64, p []byte, fr fetcher, allData map[region]io.Writer, opts *options) error {
|
||||
return b.walkChunks(allRegion, func(chunk region) error {
|
||||
var (
|
||||
base = positive(chunk.b - offset)
|
||||
lowerUnread = positive(offset - chunk.b)
|
||||
|
@ -273,14 +283,9 @@ func (b *blob) ReadAt(p []byte, offset int64, opts ...Option) (int, error) {
|
|||
expectedSize = chunk.size() - upperUnread - lowerUnread
|
||||
)
|
||||
|
||||
// Check if the content exists in the cache
|
||||
r, err := b.cache.Get(fr.genID(chunk), readAtOpts.cacheOpts...)
|
||||
if err == nil {
|
||||
defer r.Close()
|
||||
n, err := r.ReadAt(p[base:base+expectedSize], lowerUnread)
|
||||
if (err == nil || err == io.EOF) && int64(n) == expectedSize {
|
||||
return nil
|
||||
}
|
||||
// Try to read from cache first
|
||||
if err := b.readFromCache(chunk, p[base:base+expectedSize], lowerUnread, fr, opts); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We missed cache. Take it from remote registry.
|
||||
|
@ -289,21 +294,23 @@ func (b *blob) ReadAt(p []byte, offset int64, opts ...Option) (int, error) {
|
|||
allData[chunk] = newBytesWriter(p[base:base+expectedSize], lowerUnread)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Read required data
|
||||
if err := b.fetchRange(allData, &readAtOpts); err != nil {
|
||||
return 0, err
|
||||
// readFromCache attempts to read chunk data from cache
|
||||
func (b *blob) readFromCache(chunk region, dest []byte, offset int64, fr fetcher, opts *options) error {
|
||||
r, err := b.cache.Get(fr.genID(chunk), opts.cacheOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Adjust the buffer size according to the blob size
|
||||
if remain := b.size - offset; int64(len(p)) >= remain {
|
||||
if remain < 0 {
|
||||
remain = 0
|
||||
}
|
||||
p = p[:remain]
|
||||
defer r.Close()
|
||||
n, err := r.ReadAt(dest, offset)
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
if n != len(dest) {
|
||||
return fmt.Errorf("incomplete read from cache: read %d bytes, expected %d bytes", n, len(dest))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fetchRegions fetches all specified chunks from remote blob and puts it in the local cache.
|
||||
|
@ -313,11 +320,7 @@ func (b *blob) fetchRegions(allData map[region]io.Writer, fetched map[region]boo
|
|||
return nil
|
||||
}
|
||||
|
||||
// Fetcher can be suddenly updated so we take and use the snapshot of it for
|
||||
// consistency.
|
||||
b.fetcherMu.Lock()
|
||||
fr := b.fetcher
|
||||
b.fetcherMu.Unlock()
|
||||
fr := b.getFetcher()
|
||||
|
||||
// request missed regions
|
||||
var req []region
|
||||
|
@ -332,7 +335,6 @@ func (b *blob) fetchRegions(allData map[region]io.Writer, fetched map[region]boo
|
|||
fetchCtx = opts.ctx
|
||||
}
|
||||
mr, err := fr.fetch(fetchCtx, req, true)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -353,35 +355,9 @@ func (b *blob) fetchRegions(allData map[region]io.Writer, fetched map[region]boo
|
|||
return fmt.Errorf("failed to read multipart resp: %w", err)
|
||||
}
|
||||
if err := b.walkChunks(reg, func(chunk region) (retErr error) {
|
||||
id := fr.genID(chunk)
|
||||
cw, err := b.cache.Add(id, opts.cacheOpts...)
|
||||
if err != nil {
|
||||
if err := b.cacheChunkData(chunk, p, fr, allData, fetched, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
defer cw.Close()
|
||||
w := io.Writer(cw)
|
||||
|
||||
// If this chunk is one of the targets, write the content to the
|
||||
// passed reader too.
|
||||
if _, ok := fetched[chunk]; ok {
|
||||
w = io.MultiWriter(w, allData[chunk])
|
||||
}
|
||||
|
||||
// Copy the target chunk
|
||||
if _, err := io.CopyN(w, p, chunk.size()); err != nil {
|
||||
cw.Abort()
|
||||
return err
|
||||
}
|
||||
|
||||
// Add the target chunk to the cache
|
||||
if err := cw.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.fetchedRegionSetMu.Lock()
|
||||
b.fetchedRegionSet.add(chunk)
|
||||
b.fetchedRegionSetMu.Unlock()
|
||||
fetched[chunk] = true
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to get chunks: %w", err)
|
||||
|
@ -408,9 +384,6 @@ func (b *blob) fetchRange(allData map[region]io.Writer, opts *options) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// We build a key based on regions we need to fetch and pass it to singleflightGroup.Do(...)
|
||||
// to block simultaneous same requests. Once the request is finished and the data is ready,
|
||||
// all blocked callers will be unblocked and that same data will be returned by all blocked callers.
|
||||
key := makeSyncKey(allData)
|
||||
fetched := make(map[region]bool)
|
||||
_, err, shared := b.fetchedRegionGroup.Do(key, func() (interface{}, error) {
|
||||
|
@ -420,46 +393,66 @@ func (b *blob) fetchRange(allData map[region]io.Writer, opts *options) error {
|
|||
// When unblocked try to read from cache in case if there were no errors
|
||||
// If we fail reading from cache, fetch from remote registry again
|
||||
if err == nil && shared {
|
||||
for reg := range allData {
|
||||
if _, ok := fetched[reg]; ok {
|
||||
continue
|
||||
}
|
||||
err = b.walkChunks(reg, func(chunk region) error {
|
||||
b.fetcherMu.Lock()
|
||||
fr := b.fetcher
|
||||
b.fetcherMu.Unlock()
|
||||
|
||||
// Check if the content exists in the cache
|
||||
// And if exists, read from cache
|
||||
r, err := b.cache.Get(fr.genID(chunk), opts.cacheOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
rr := io.NewSectionReader(r, 0, chunk.size())
|
||||
|
||||
// Copy the target chunk
|
||||
b.fetchedRegionCopyMu.Lock()
|
||||
defer b.fetchedRegionCopyMu.Unlock()
|
||||
if _, err := io.CopyN(allData[chunk], rr, chunk.size()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if we cannot read the data from cache, do fetch again
|
||||
if err != nil {
|
||||
return b.fetchRange(allData, opts)
|
||||
if err := b.handleSharedFetch(allData, fetched, opts); err != nil {
|
||||
return b.fetchRange(allData, opts) // retry on error
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// handleSharedFetch handles the case when multiple goroutines share the same fetch result
|
||||
func (b *blob) handleSharedFetch(allData map[region]io.Writer, fetched map[region]bool, opts *options) error {
|
||||
for reg := range allData {
|
||||
if _, ok := fetched[reg]; ok {
|
||||
continue
|
||||
}
|
||||
if err := b.copyFetchedChunks(reg, allData, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyFetchedChunks copies fetched chunks from cache to target writer
|
||||
func (b *blob) copyFetchedChunks(reg region, allData map[region]io.Writer, opts *options) error {
|
||||
return b.walkChunks(reg, func(chunk region) error {
|
||||
fr := b.getFetcher()
|
||||
r, err := b.cache.Get(fr.genID(chunk), opts.cacheOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
b.fetchedRegionCopyMu.Lock()
|
||||
defer b.fetchedRegionCopyMu.Unlock()
|
||||
|
||||
if _, err := io.CopyN(allData[chunk], io.NewSectionReader(r, 0, chunk.size()), chunk.size()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// getFetcher safely gets the current fetcher
|
||||
// Fetcher can be suddenly updated so we take and use the snapshot of it for consistency.
|
||||
func (b *blob) getFetcher() fetcher {
|
||||
b.fetcherMu.Lock()
|
||||
defer b.fetcherMu.Unlock()
|
||||
return b.fetcher
|
||||
}
|
||||
|
||||
// adjustBufferSize adjusts buffer size according to the blob size
|
||||
func (b *blob) adjustBufferSize(p []byte, offset int64) int {
|
||||
if remain := b.size - offset; int64(len(p)) >= remain {
|
||||
if remain < 0 {
|
||||
remain = 0
|
||||
}
|
||||
p = p[:remain]
|
||||
}
|
||||
return len(p)
|
||||
}
|
||||
|
||||
type walkFunc func(reg region) error
|
||||
|
||||
// walkChunks walks chunks from begin to end in order in the specified region.
|
||||
|
@ -533,3 +526,34 @@ func positive(n int64) int64 {
|
|||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// cacheChunkData handles caching of chunk data
|
||||
func (b *blob) cacheChunkData(chunk region, r io.Reader, fr fetcher, allData map[region]io.Writer, fetched map[region]bool, opts *options) error {
|
||||
id := fr.genID(chunk)
|
||||
cw, err := b.cache.Add(id, opts.cacheOpts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create cache writer: %w", err)
|
||||
}
|
||||
defer cw.Close()
|
||||
|
||||
w := io.Writer(cw)
|
||||
if _, ok := fetched[chunk]; ok {
|
||||
w = io.MultiWriter(w, allData[chunk])
|
||||
}
|
||||
|
||||
if _, err := io.CopyN(w, r, chunk.size()); err != nil {
|
||||
cw.Abort()
|
||||
return fmt.Errorf("failed to write chunk data: %w", err)
|
||||
}
|
||||
|
||||
if err := cw.Commit(); err != nil {
|
||||
return fmt.Errorf("failed to commit chunk: %w", err)
|
||||
}
|
||||
|
||||
b.fetchedRegionSetMu.Lock()
|
||||
b.fetchedRegionSet.add(chunk)
|
||||
b.fetchedRegionSetMu.Unlock()
|
||||
fetched[chunk] = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -609,7 +609,7 @@ func TestCheckInterval(t *testing.T) {
|
|||
if !tr.called {
|
||||
return b.lastCheck, false
|
||||
}
|
||||
if !(b.lastCheck.After(beforeUpdate) && b.lastCheck.Before(afterUpdate)) {
|
||||
if !b.lastCheck.After(beforeUpdate) || !b.lastCheck.Before(afterUpdate) {
|
||||
t.Errorf("%q: updated time must be after %q and before %q but %q", name, beforeUpdate, afterUpdate, b.lastCheck)
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
@ -46,7 +47,6 @@ import (
|
|||
"github.com/containerd/stargz-snapshotter/fs/config"
|
||||
commonmetrics "github.com/containerd/stargz-snapshotter/fs/metrics/common"
|
||||
"github.com/containerd/stargz-snapshotter/fs/source"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
rhttp "github.com/hashicorp/go-retryablehttp"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
@ -122,19 +122,19 @@ func (r *Resolver) Resolve(ctx context.Context, hosts source.RegistryHosts, refs
|
|||
func (r *Resolver) resolveFetcher(ctx context.Context, hosts source.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor) (f fetcher, size int64, err error) {
|
||||
blobConfig := &r.blobConfig
|
||||
fc := &fetcherConfig{
|
||||
hosts: hosts,
|
||||
refspec: refspec,
|
||||
desc: desc,
|
||||
maxRetries: blobConfig.MaxRetries,
|
||||
minWaitMSec: time.Duration(blobConfig.MinWaitMSec) * time.Millisecond,
|
||||
maxWaitMSec: time.Duration(blobConfig.MaxWaitMSec) * time.Millisecond,
|
||||
hosts: hosts,
|
||||
refspec: refspec,
|
||||
desc: desc,
|
||||
maxRetries: blobConfig.MaxRetries,
|
||||
minWait: time.Duration(blobConfig.MinWaitMSec) * time.Millisecond,
|
||||
maxWait: time.Duration(blobConfig.MaxWaitMSec) * time.Millisecond,
|
||||
}
|
||||
var handlersErr error
|
||||
var errs []error
|
||||
for name, p := range r.handlers {
|
||||
// TODO: allow to configure the selection of readers based on the hostname in refspec
|
||||
r, size, err := p.Handle(ctx, desc)
|
||||
if err != nil {
|
||||
handlersErr = multierror.Append(handlersErr, err)
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
log.G(ctx).WithField("handler name", name).WithField("ref", refspec.String()).WithField("digest", desc.Digest).
|
||||
|
@ -142,6 +142,8 @@ func (r *Resolver) resolveFetcher(ctx context.Context, hosts source.RegistryHost
|
|||
return &remoteFetcher{r}, size, nil
|
||||
}
|
||||
|
||||
handlersErr := errors.Join(errs...)
|
||||
|
||||
log.G(ctx).WithError(handlersErr).WithField("ref", refspec.String()).WithField("digest", desc.Digest).Debugf("using default handler")
|
||||
hf, size, err := newHTTPFetcher(ctx, fc)
|
||||
if err != nil {
|
||||
|
@ -154,12 +156,12 @@ func (r *Resolver) resolveFetcher(ctx context.Context, hosts source.RegistryHost
|
|||
}
|
||||
|
||||
type fetcherConfig struct {
|
||||
hosts source.RegistryHosts
|
||||
refspec reference.Spec
|
||||
desc ocispec.Descriptor
|
||||
maxRetries int
|
||||
minWaitMSec time.Duration
|
||||
maxWaitMSec time.Duration
|
||||
hosts source.RegistryHosts
|
||||
refspec reference.Spec
|
||||
desc ocispec.Descriptor
|
||||
maxRetries int
|
||||
minWait time.Duration
|
||||
maxWait time.Duration
|
||||
}
|
||||
|
||||
func jitter(duration time.Duration) time.Duration {
|
||||
|
@ -200,7 +202,7 @@ func newHTTPFetcher(ctx context.Context, fc *fetcherConfig) (*httpFetcher, int64
|
|||
}
|
||||
desc := fc.desc
|
||||
if desc.Digest.String() == "" {
|
||||
return nil, 0, fmt.Errorf("Digest is mandatory in layer descriptor")
|
||||
return nil, 0, fmt.Errorf("digest is mandatory in layer descriptor")
|
||||
}
|
||||
digest := desc.Digest
|
||||
pullScope, err := docker.RepositoryScope(fc.refspec, false)
|
||||
|
@ -223,8 +225,8 @@ func newHTTPFetcher(ctx context.Context, fc *fetcherConfig) (*httpFetcher, int64
|
|||
timeout := host.Client.Timeout
|
||||
if rt, ok := tr.(*rhttp.RoundTripper); ok {
|
||||
rt.Client.RetryMax = fc.maxRetries
|
||||
rt.Client.RetryWaitMin = fc.minWaitMSec
|
||||
rt.Client.RetryWaitMax = fc.maxWaitMSec
|
||||
rt.Client.RetryWaitMin = fc.minWait
|
||||
rt.Client.RetryWaitMax = fc.maxWait
|
||||
rt.Client.Backoff = backoffStrategy
|
||||
rt.Client.CheckRetry = retryStrategy
|
||||
timeout = rt.Client.HTTPClient.Timeout
|
||||
|
@ -406,9 +408,10 @@ func getSize(ctx context.Context, url string, tr http.RoundTripper, timeout time
|
|||
res.Body.Close()
|
||||
}()
|
||||
|
||||
if res.StatusCode == http.StatusOK {
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
return strconv.ParseInt(res.Header.Get("Content-Length"), 10, 64)
|
||||
} else if res.StatusCode == http.StatusPartialContent {
|
||||
case http.StatusPartialContent:
|
||||
_, size, err := parseRange(res.Header.Get("Content-Range"))
|
||||
return size, err
|
||||
}
|
||||
|
@ -556,9 +559,10 @@ func (f *httpFetcher) check() error {
|
|||
io.Copy(io.Discard, res.Body)
|
||||
res.Body.Close()
|
||||
}()
|
||||
if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusPartialContent {
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK, http.StatusPartialContent:
|
||||
return nil
|
||||
} else if res.StatusCode == http.StatusForbidden {
|
||||
case http.StatusForbidden:
|
||||
// Try to re-redirect this blob
|
||||
rCtx := context.Background()
|
||||
if f.timeout > 0 {
|
||||
|
|
|
@ -0,0 +1,566 @@
|
|||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: api.proto
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type StatusRequest struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *StatusRequest) Reset() { *m = StatusRequest{} }
|
||||
func (m *StatusRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*StatusRequest) ProtoMessage() {}
|
||||
func (*StatusRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_00212fb1f9d3bf1c, []int{0}
|
||||
}
|
||||
func (m *StatusRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_StatusRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *StatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_StatusRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *StatusRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_StatusRequest.Merge(m, src)
|
||||
}
|
||||
func (m *StatusRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_StatusRequest.Size(m)
|
||||
}
|
||||
func (m *StatusRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_StatusRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_StatusRequest proto.InternalMessageInfo
|
||||
|
||||
type InitRequest struct {
|
||||
Root string `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"`
|
||||
Config []byte `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *InitRequest) Reset() { *m = InitRequest{} }
|
||||
func (m *InitRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*InitRequest) ProtoMessage() {}
|
||||
func (*InitRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_00212fb1f9d3bf1c, []int{1}
|
||||
}
|
||||
func (m *InitRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_InitRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *InitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_InitRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *InitRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_InitRequest.Merge(m, src)
|
||||
}
|
||||
func (m *InitRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_InitRequest.Size(m)
|
||||
}
|
||||
func (m *InitRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_InitRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_InitRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *InitRequest) GetRoot() string {
|
||||
if m != nil {
|
||||
return m.Root
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *InitRequest) GetConfig() []byte {
|
||||
if m != nil {
|
||||
return m.Config
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MountRequest struct {
|
||||
Mountpoint string `protobuf:"bytes,1,opt,name=mountpoint,proto3" json:"mountpoint,omitempty"`
|
||||
Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *MountRequest) Reset() { *m = MountRequest{} }
|
||||
func (m *MountRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*MountRequest) ProtoMessage() {}
|
||||
func (*MountRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_00212fb1f9d3bf1c, []int{2}
|
||||
}
|
||||
func (m *MountRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_MountRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *MountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_MountRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *MountRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_MountRequest.Merge(m, src)
|
||||
}
|
||||
func (m *MountRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_MountRequest.Size(m)
|
||||
}
|
||||
func (m *MountRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_MountRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_MountRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *MountRequest) GetMountpoint() string {
|
||||
if m != nil {
|
||||
return m.Mountpoint
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *MountRequest) GetLabels() map[string]string {
|
||||
if m != nil {
|
||||
return m.Labels
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CheckRequest struct {
|
||||
Mountpoint string `protobuf:"bytes,1,opt,name=mountpoint,proto3" json:"mountpoint,omitempty"`
|
||||
Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CheckRequest) Reset() { *m = CheckRequest{} }
|
||||
func (m *CheckRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*CheckRequest) ProtoMessage() {}
|
||||
func (*CheckRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_00212fb1f9d3bf1c, []int{3}
|
||||
}
|
||||
func (m *CheckRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CheckRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CheckRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CheckRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CheckRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CheckRequest.Merge(m, src)
|
||||
}
|
||||
func (m *CheckRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_CheckRequest.Size(m)
|
||||
}
|
||||
func (m *CheckRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CheckRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CheckRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *CheckRequest) GetMountpoint() string {
|
||||
if m != nil {
|
||||
return m.Mountpoint
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *CheckRequest) GetLabels() map[string]string {
|
||||
if m != nil {
|
||||
return m.Labels
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UnmountRequest struct {
|
||||
Mountpoint string `protobuf:"bytes,1,opt,name=mountpoint,proto3" json:"mountpoint,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *UnmountRequest) Reset() { *m = UnmountRequest{} }
|
||||
func (m *UnmountRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*UnmountRequest) ProtoMessage() {}
|
||||
func (*UnmountRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_00212fb1f9d3bf1c, []int{4}
|
||||
}
|
||||
func (m *UnmountRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_UnmountRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *UnmountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_UnmountRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *UnmountRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_UnmountRequest.Merge(m, src)
|
||||
}
|
||||
func (m *UnmountRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_UnmountRequest.Size(m)
|
||||
}
|
||||
func (m *UnmountRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_UnmountRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_UnmountRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *UnmountRequest) GetMountpoint() string {
|
||||
if m != nil {
|
||||
return m.Mountpoint
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type StatusResponse struct {
|
||||
Status int32 `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *StatusResponse) Reset() { *m = StatusResponse{} }
|
||||
func (m *StatusResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*StatusResponse) ProtoMessage() {}
|
||||
func (*StatusResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_00212fb1f9d3bf1c, []int{5}
|
||||
}
|
||||
func (m *StatusResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_StatusResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *StatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_StatusResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *StatusResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_StatusResponse.Merge(m, src)
|
||||
}
|
||||
func (m *StatusResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_StatusResponse.Size(m)
|
||||
}
|
||||
func (m *StatusResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_StatusResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_StatusResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *StatusResponse) GetStatus() int32 {
|
||||
if m != nil {
|
||||
return m.Status
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_00212fb1f9d3bf1c, []int{6}
|
||||
}
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
}
|
||||
func (m *Response) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Response.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Response proto.InternalMessageInfo
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*StatusRequest)(nil), "fusemanager.StatusRequest")
|
||||
proto.RegisterType((*InitRequest)(nil), "fusemanager.InitRequest")
|
||||
proto.RegisterType((*MountRequest)(nil), "fusemanager.MountRequest")
|
||||
proto.RegisterMapType((map[string]string)(nil), "fusemanager.MountRequest.LabelsEntry")
|
||||
proto.RegisterType((*CheckRequest)(nil), "fusemanager.CheckRequest")
|
||||
proto.RegisterMapType((map[string]string)(nil), "fusemanager.CheckRequest.LabelsEntry")
|
||||
proto.RegisterType((*UnmountRequest)(nil), "fusemanager.UnmountRequest")
|
||||
proto.RegisterType((*StatusResponse)(nil), "fusemanager.StatusResponse")
|
||||
proto.RegisterType((*Response)(nil), "fusemanager.Response")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("api.proto", fileDescriptor_00212fb1f9d3bf1c) }
|
||||
|
||||
var fileDescriptor_00212fb1f9d3bf1c = []byte{
|
||||
// 386 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0x51, 0x4b, 0xf3, 0x30,
|
||||
0x14, 0xa5, 0xdd, 0xd6, 0xef, 0xdb, 0xed, 0x9c, 0x12, 0x54, 0x6a, 0x05, 0x19, 0x05, 0xa1, 0x2f,
|
||||
0x6b, 0x65, 0x3e, 0xe8, 0x84, 0x3d, 0xa8, 0x28, 0x08, 0xee, 0xa5, 0xc3, 0x17, 0xdf, 0xb2, 0x92,
|
||||
0x75, 0x65, 0x6b, 0x52, 0x9b, 0x74, 0x30, 0x7f, 0x91, 0xff, 0xc5, 0x3f, 0x25, 0xcd, 0xba, 0x91,
|
||||
0x8a, 0x13, 0x84, 0xbd, 0xe5, 0x24, 0xf7, 0xdc, 0x9e, 0x7b, 0xcf, 0x29, 0x34, 0x71, 0x1a, 0x7b,
|
||||
0x69, 0xc6, 0x04, 0x43, 0xe6, 0x24, 0xe7, 0x24, 0xc1, 0x14, 0x47, 0x24, 0x73, 0xf6, 0x61, 0x6f,
|
||||
0x24, 0xb0, 0xc8, 0x79, 0x40, 0xde, 0x72, 0xc2, 0x85, 0xd3, 0x07, 0xf3, 0x89, 0xc6, 0xa2, 0x84,
|
||||
0x08, 0x41, 0x3d, 0x63, 0x4c, 0x58, 0x5a, 0x47, 0x73, 0x9b, 0x81, 0x3c, 0xa3, 0x63, 0x30, 0x42,
|
||||
0x46, 0x27, 0x71, 0x64, 0xe9, 0x1d, 0xcd, 0x6d, 0x05, 0x25, 0x72, 0x3e, 0x34, 0x68, 0x0d, 0x59,
|
||||
0x4e, 0x37, 0xe4, 0x33, 0x80, 0xa4, 0xc0, 0x29, 0x8b, 0xe9, 0xba, 0x85, 0x72, 0x83, 0x06, 0x60,
|
||||
0xcc, 0xf1, 0x98, 0xcc, 0xb9, 0xa5, 0x77, 0x6a, 0xae, 0xd9, 0x3b, 0xf7, 0x14, 0x69, 0x9e, 0xda,
|
||||
0xca, 0x7b, 0x96, 0x75, 0x0f, 0x54, 0x64, 0xcb, 0xa0, 0x24, 0xd9, 0x7d, 0x30, 0x95, 0x6b, 0x74,
|
||||
0x00, 0xb5, 0x19, 0x59, 0x96, 0x9f, 0x29, 0x8e, 0xe8, 0x10, 0x1a, 0x0b, 0x3c, 0xcf, 0x89, 0xd4,
|
||||
0xd9, 0x0c, 0x56, 0xe0, 0x46, 0xbf, 0xd6, 0xa4, 0xd4, 0xfb, 0x29, 0x09, 0x67, 0xbb, 0x91, 0xaa,
|
||||
0xb6, 0xda, 0xb5, 0xd4, 0x0b, 0x68, 0xbf, 0xd0, 0xe4, 0x0f, 0x6b, 0x75, 0x5c, 0x68, 0xaf, 0x3d,
|
||||
0xe5, 0x29, 0xa3, 0x9c, 0x14, 0x8e, 0x71, 0x79, 0x23, 0xab, 0x1b, 0x41, 0x89, 0x1c, 0x80, 0xff,
|
||||
0xeb, 0x9a, 0xde, 0xa7, 0x0e, 0xd6, 0x48, 0xe0, 0x2c, 0x7a, 0x7f, 0xcc, 0x39, 0x19, 0xae, 0x26,
|
||||
0x1b, 0x91, 0x6c, 0x11, 0x87, 0x04, 0xdd, 0x82, 0xb1, 0x6a, 0x89, 0xec, 0xca, 0xe0, 0x95, 0xec,
|
||||
0xd8, 0xa7, 0x3f, 0xbe, 0x95, 0x1a, 0xae, 0xa0, 0x5e, 0x04, 0x0b, 0x59, 0x95, 0x22, 0x25, 0x6b,
|
||||
0xf6, 0x51, 0xe5, 0x65, 0x43, 0xec, 0x43, 0x43, 0x46, 0x01, 0x9d, 0x6c, 0x8d, 0xc7, 0x2f, 0x54,
|
||||
0x69, 0xcd, 0x37, 0xaa, 0x6a, 0xd7, 0x36, 0xea, 0x00, 0xfe, 0x95, 0x6b, 0x47, 0xd5, 0xb1, 0xaa,
|
||||
0x66, 0x6c, 0xa1, 0xdf, 0xf9, 0xaf, 0xdd, 0x28, 0x16, 0xd3, 0x7c, 0xec, 0x85, 0x2c, 0xf1, 0xb9,
|
||||
0xdc, 0x6b, 0x97, 0x53, 0x9c, 0xf2, 0x29, 0x13, 0x82, 0x64, 0xbe, 0xc2, 0xf2, 0x71, 0x1a, 0x8f,
|
||||
0x0d, 0xf9, 0x73, 0x5e, 0x7e, 0x05, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x24, 0xe1, 0x41, 0xa9, 0x03,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// StargzFuseManagerServiceClient is the client API for StargzFuseManagerService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type StargzFuseManagerServiceClient interface {
|
||||
Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error)
|
||||
Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Response, error)
|
||||
Mount(ctx context.Context, in *MountRequest, opts ...grpc.CallOption) (*Response, error)
|
||||
Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*Response, error)
|
||||
Unmount(ctx context.Context, in *UnmountRequest, opts ...grpc.CallOption) (*Response, error)
|
||||
}
|
||||
|
||||
type stargzFuseManagerServiceClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewStargzFuseManagerServiceClient(cc *grpc.ClientConn) StargzFuseManagerServiceClient {
|
||||
return &stargzFuseManagerServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *stargzFuseManagerServiceClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) {
|
||||
out := new(StatusResponse)
|
||||
err := c.cc.Invoke(ctx, "/fusemanager.StargzFuseManagerService/Status", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *stargzFuseManagerServiceClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/fusemanager.StargzFuseManagerService/Init", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *stargzFuseManagerServiceClient) Mount(ctx context.Context, in *MountRequest, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/fusemanager.StargzFuseManagerService/Mount", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *stargzFuseManagerServiceClient) Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/fusemanager.StargzFuseManagerService/Check", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *stargzFuseManagerServiceClient) Unmount(ctx context.Context, in *UnmountRequest, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/fusemanager.StargzFuseManagerService/Unmount", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// StargzFuseManagerServiceServer is the server API for StargzFuseManagerService service.
|
||||
type StargzFuseManagerServiceServer interface {
|
||||
Status(context.Context, *StatusRequest) (*StatusResponse, error)
|
||||
Init(context.Context, *InitRequest) (*Response, error)
|
||||
Mount(context.Context, *MountRequest) (*Response, error)
|
||||
Check(context.Context, *CheckRequest) (*Response, error)
|
||||
Unmount(context.Context, *UnmountRequest) (*Response, error)
|
||||
}
|
||||
|
||||
// UnimplementedStargzFuseManagerServiceServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedStargzFuseManagerServiceServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedStargzFuseManagerServiceServer) Status(ctx context.Context, req *StatusRequest) (*StatusResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Status not implemented")
|
||||
}
|
||||
func (*UnimplementedStargzFuseManagerServiceServer) Init(ctx context.Context, req *InitRequest) (*Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Init not implemented")
|
||||
}
|
||||
func (*UnimplementedStargzFuseManagerServiceServer) Mount(ctx context.Context, req *MountRequest) (*Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Mount not implemented")
|
||||
}
|
||||
func (*UnimplementedStargzFuseManagerServiceServer) Check(ctx context.Context, req *CheckRequest) (*Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Check not implemented")
|
||||
}
|
||||
func (*UnimplementedStargzFuseManagerServiceServer) Unmount(ctx context.Context, req *UnmountRequest) (*Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Unmount not implemented")
|
||||
}
|
||||
|
||||
func RegisterStargzFuseManagerServiceServer(s *grpc.Server, srv StargzFuseManagerServiceServer) {
|
||||
s.RegisterService(&_StargzFuseManagerService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _StargzFuseManagerService_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StatusRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StargzFuseManagerServiceServer).Status(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/fusemanager.StargzFuseManagerService/Status",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StargzFuseManagerServiceServer).Status(ctx, req.(*StatusRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _StargzFuseManagerService_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(InitRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StargzFuseManagerServiceServer).Init(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/fusemanager.StargzFuseManagerService/Init",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StargzFuseManagerServiceServer).Init(ctx, req.(*InitRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _StargzFuseManagerService_Mount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MountRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StargzFuseManagerServiceServer).Mount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/fusemanager.StargzFuseManagerService/Mount",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StargzFuseManagerServiceServer).Mount(ctx, req.(*MountRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _StargzFuseManagerService_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CheckRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StargzFuseManagerServiceServer).Check(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/fusemanager.StargzFuseManagerService/Check",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StargzFuseManagerServiceServer).Check(ctx, req.(*CheckRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _StargzFuseManagerService_Unmount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UnmountRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StargzFuseManagerServiceServer).Unmount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/fusemanager.StargzFuseManagerService/Unmount",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StargzFuseManagerServiceServer).Unmount(ctx, req.(*UnmountRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _StargzFuseManagerService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "fusemanager.StargzFuseManagerService",
|
||||
HandlerType: (*StargzFuseManagerServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Status",
|
||||
Handler: _StargzFuseManagerService_Status_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Init",
|
||||
Handler: _StargzFuseManagerService_Init_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Mount",
|
||||
Handler: _StargzFuseManagerService_Mount_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Check",
|
||||
Handler: _StargzFuseManagerService_Check_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Unmount",
|
||||
Handler: _StargzFuseManagerService_Unmount_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "api.proto",
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "github.com/stargz-snapshotter/fusemanager/api";
|
||||
|
||||
package fusemanager;
|
||||
|
||||
service StargzFuseManagerService {
|
||||
rpc Status (StatusRequest) returns (StatusResponse);
|
||||
rpc Init (InitRequest) returns (Response);
|
||||
rpc Mount (MountRequest) returns (Response);
|
||||
rpc Check (CheckRequest) returns (Response);
|
||||
rpc Unmount (UnmountRequest) returns (Response);
|
||||
}
|
||||
|
||||
message StatusRequest {
|
||||
}
|
||||
|
||||
message InitRequest {
|
||||
string root = 1;
|
||||
bytes config = 2;
|
||||
}
|
||||
|
||||
message MountRequest {
|
||||
string mountpoint = 1;
|
||||
map<string, string> labels = 2;
|
||||
}
|
||||
|
||||
message CheckRequest {
|
||||
string mountpoint = 1;
|
||||
map<string, string> labels = 2;
|
||||
}
|
||||
|
||||
message UnmountRequest {
|
||||
string mountpoint = 1;
|
||||
}
|
||||
|
||||
message StatusResponse {
|
||||
int32 status = 1;
|
||||
}
|
||||
|
||||
message Response {
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
//go:generate protoc --gogo_out=paths=source_relative,plugins=grpc:. api.proto
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fusemanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/containerd/containerd/v2/defaults"
|
||||
"github.com/containerd/containerd/v2/pkg/dialer"
|
||||
"github.com/containerd/log"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
pb "github.com/containerd/stargz-snapshotter/fusemanager/api"
|
||||
"github.com/containerd/stargz-snapshotter/snapshot"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
client pb.StargzFuseManagerServiceClient
|
||||
}
|
||||
|
||||
func NewManagerClient(ctx context.Context, root, socket string, config *Config) (snapshot.FileSystem, error) {
|
||||
grpcCli, err := newClient(socket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := &Client{
|
||||
client: grpcCli,
|
||||
}
|
||||
|
||||
err = client.init(ctx, root, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func newClient(socket string) (pb.StargzFuseManagerServiceClient, error) {
|
||||
connParams := grpc.ConnectParams{
|
||||
Backoff: backoff.DefaultConfig,
|
||||
}
|
||||
gopts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithConnectParams(connParams),
|
||||
grpc.WithContextDialer(dialer.ContextDialer),
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize),
|
||||
grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize),
|
||||
),
|
||||
}
|
||||
|
||||
conn, err := grpc.NewClient(fmt.Sprintf("unix://%s", socket), gopts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pb.NewStargzFuseManagerServiceClient(conn), nil
|
||||
}
|
||||
|
||||
func (cli *Client) init(ctx context.Context, root string, config *Config) error {
|
||||
configBytes, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := &pb.InitRequest{
|
||||
Root: root,
|
||||
Config: configBytes,
|
||||
}
|
||||
|
||||
_, err = cli.client.Init(ctx, req)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to call Init")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *Client) Mount(ctx context.Context, mountpoint string, labels map[string]string) error {
|
||||
req := &pb.MountRequest{
|
||||
Mountpoint: mountpoint,
|
||||
Labels: labels,
|
||||
}
|
||||
|
||||
_, err := cli.client.Mount(ctx, req)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to call Mount")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *Client) Check(ctx context.Context, mountpoint string, labels map[string]string) error {
|
||||
req := &pb.CheckRequest{
|
||||
Mountpoint: mountpoint,
|
||||
Labels: labels,
|
||||
}
|
||||
|
||||
_, err := cli.client.Check(ctx, req)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to call Check")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *Client) Unmount(ctx context.Context, mountpoint string) error {
|
||||
req := &pb.UnmountRequest{
|
||||
Mountpoint: mountpoint,
|
||||
}
|
||||
|
||||
_, err := cli.client.Unmount(ctx, req)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to call Unmount")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fusemanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
golog "log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
pb "github.com/containerd/stargz-snapshotter/fusemanager/api"
|
||||
"github.com/containerd/stargz-snapshotter/version"
|
||||
)
|
||||
|
||||
var (
|
||||
debugFlag bool
|
||||
versionFlag bool
|
||||
fuseStoreAddr string
|
||||
address string
|
||||
logLevel string
|
||||
logPath string
|
||||
action string
|
||||
)
|
||||
|
||||
func parseFlags() {
|
||||
flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs")
|
||||
flag.BoolVar(&versionFlag, "v", false, "show the fusemanager version and exit")
|
||||
flag.StringVar(&action, "action", "", "action of fusemanager")
|
||||
flag.StringVar(&fuseStoreAddr, "fusestore-path", "/var/lib/containerd-stargz-grpc/fusestore.db", "address for the fusemanager's store")
|
||||
flag.StringVar(&address, "address", "/run/containerd-stargz-grpc/fuse-manager.sock", "address for the fusemanager's gRPC socket")
|
||||
flag.StringVar(&logLevel, "log-level", logrus.InfoLevel.String(), "set the logging level [trace, debug, info, warn, error, fatal, panic]")
|
||||
flag.StringVar(&logPath, "log-path", "", "path to fusemanager's logs, no log recorded if empty")
|
||||
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func Run() {
|
||||
if err := run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to run fusemanager: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
parseFlags()
|
||||
if versionFlag {
|
||||
fmt.Printf("%s:\n", os.Args[0])
|
||||
fmt.Println(" Version: ", version.Version)
|
||||
fmt.Println(" Revision:", version.Revision)
|
||||
fmt.Println("")
|
||||
return nil
|
||||
}
|
||||
|
||||
if fuseStoreAddr == "" || address == "" {
|
||||
return fmt.Errorf("fusemanager fusestore and socket path cannot be empty")
|
||||
}
|
||||
|
||||
ctx := log.WithLogger(context.Background(), log.L)
|
||||
|
||||
switch action {
|
||||
case "start":
|
||||
return startNew(ctx, logPath, address, fuseStoreAddr, logLevel)
|
||||
default:
|
||||
return runFuseManager(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func startNew(ctx context.Context, logPath, address, fusestore, logLevel string) error {
|
||||
self, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-address", address,
|
||||
"-fusestore-path", fusestore,
|
||||
"-log-level", logLevel,
|
||||
}
|
||||
|
||||
// we use shim-like approach to start new fusemanager process by self-invoking in the background
|
||||
// and detach it from parent
|
||||
cmd := exec.CommandContext(ctx, self, args...)
|
||||
cmd.Dir = cwd
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
|
||||
if logPath != "" {
|
||||
err := os.Remove(logPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
file, err := os.Create(logPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Stdout = file
|
||||
cmd.Stderr = file
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
go cmd.Wait()
|
||||
|
||||
if ready, err := waitUntilReady(ctx); err != nil || !ready {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start new fusemanager: %w", err)
|
||||
}
|
||||
if !ready {
|
||||
return fmt.Errorf("failed to start new fusemanager, fusemanager not ready")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// waitUntilReady waits until fusemanager is ready to accept requests
|
||||
func waitUntilReady(ctx context.Context) (bool, error) {
|
||||
grpcCli, err := newClient(address)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := grpcCli.Status(ctx, &pb.StatusRequest{})
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to call Status")
|
||||
return false, err
|
||||
}
|
||||
|
||||
if resp.Status == FuseManagerNotReady {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func runFuseManager(ctx context.Context) error {
|
||||
lvl, err := logrus.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to prepare logger: %w", err)
|
||||
}
|
||||
|
||||
logrus.SetLevel(lvl)
|
||||
logrus.SetFormatter(&logrus.JSONFormatter{
|
||||
TimestampFormat: log.RFC3339NanoFixed,
|
||||
})
|
||||
|
||||
golog.SetOutput(log.G(ctx).WriterLevel(logrus.DebugLevel))
|
||||
|
||||
// Prepare the directory for the socket
|
||||
if err := os.MkdirAll(filepath.Dir(address), 0700); err != nil {
|
||||
return fmt.Errorf("failed to create directory %s: %w", filepath.Dir(address), err)
|
||||
}
|
||||
|
||||
// Try to remove the socket file to avoid EADDRINUSE
|
||||
if err := os.Remove(address); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove old socket file: %w", err)
|
||||
}
|
||||
|
||||
l, err := net.Listen("unix", address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen socket: %w", err)
|
||||
}
|
||||
|
||||
server := grpc.NewServer()
|
||||
fm, err := NewFuseManager(ctx, l, server, fuseStoreAddr, address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to configure manager server: %w", err)
|
||||
}
|
||||
|
||||
pb.RegisterStargzFuseManagerServiceServer(server, fm)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
if err := server.Serve(l); err != nil {
|
||||
errCh <- fmt.Errorf("error on serving via socket %q: %w", address, err)
|
||||
}
|
||||
}()
|
||||
|
||||
var s os.Signal
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, unix.SIGINT, unix.SIGTERM)
|
||||
select {
|
||||
case s = <-sigCh:
|
||||
log.G(ctx).Infof("Got %v", s)
|
||||
case err := <-errCh:
|
||||
log.G(ctx).WithError(err).Warnf("error during running the server")
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
if err = fm.Close(ctx); err != nil {
|
||||
return fmt.Errorf("failed to close fuse manager: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartFuseManager(ctx context.Context, executable, address, fusestore, logLevel, logPath string) (newlyStarted bool, err error) {
|
||||
// if socket exists, do not start it
|
||||
if _, err := os.Stat(address); err == nil {
|
||||
return false, nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(executable); err != nil {
|
||||
return false, fmt.Errorf("failed to stat fusemanager binary: %q", executable)
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-action", "start",
|
||||
"-address", address,
|
||||
"-fusestore-path", fusestore,
|
||||
"-log-level", logLevel,
|
||||
"-log-path", logPath,
|
||||
}
|
||||
|
||||
cmd := exec.Command(executable, args...)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fusemanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
pb "github.com/containerd/stargz-snapshotter/fusemanager/api"
|
||||
"github.com/containerd/stargz-snapshotter/service"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// mockFileSystem implements snapshot.FileSystem for testing
|
||||
type mockFileSystem struct {
|
||||
t *testing.T
|
||||
mountErr error
|
||||
checkErr error
|
||||
unmountErr error
|
||||
mountPoints map[string]bool
|
||||
checkCalled bool
|
||||
mountCalled bool
|
||||
unmountCalled bool
|
||||
}
|
||||
|
||||
func newMockFileSystem(t *testing.T) *mockFileSystem {
|
||||
return &mockFileSystem{
|
||||
t: t,
|
||||
mountPoints: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *mockFileSystem) Mount(ctx context.Context, mountpoint string, labels map[string]string) error {
|
||||
fs.mountCalled = true
|
||||
if fs.mountErr != nil {
|
||||
return fs.mountErr
|
||||
}
|
||||
fs.mountPoints[mountpoint] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *mockFileSystem) Check(ctx context.Context, mountpoint string, labels map[string]string) error {
|
||||
fs.checkCalled = true
|
||||
if fs.checkErr != nil {
|
||||
return fs.checkErr
|
||||
}
|
||||
if _, ok := fs.mountPoints[mountpoint]; !ok {
|
||||
return fmt.Errorf("mountpoint %s not found", mountpoint)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *mockFileSystem) Unmount(ctx context.Context, mountpoint string) error {
|
||||
fs.unmountCalled = true
|
||||
if fs.unmountErr != nil {
|
||||
return fs.unmountErr
|
||||
}
|
||||
delete(fs.mountPoints, mountpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
// mockServer embeds Server struct and overrides Init method
|
||||
type mockServer struct {
|
||||
*Server
|
||||
initCalled bool
|
||||
initErr error
|
||||
}
|
||||
|
||||
func newMockServer(ctx context.Context, listener net.Listener, server *grpc.Server, fuseStoreAddr, serverAddr string) (*mockServer, error) {
|
||||
s, err := NewFuseManager(ctx, listener, server, fuseStoreAddr, serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mockServer{Server: s}, nil
|
||||
}
|
||||
|
||||
// Init overrides Server.Init to avoid actual initialization
|
||||
func (s *mockServer) Init(ctx context.Context, req *pb.InitRequest) (*pb.Response, error) {
|
||||
s.initCalled = true
|
||||
if s.initErr != nil {
|
||||
return nil, s.initErr
|
||||
}
|
||||
|
||||
// Set only required fields
|
||||
s.root = req.Root
|
||||
config := &Config{}
|
||||
if err := json.Unmarshal(req.Config, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.config = config
|
||||
s.status = FuseManagerReady
|
||||
|
||||
return &pb.Response{}, nil
|
||||
}
|
||||
|
||||
func TestFuseManager(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "fusemanager-test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
||||
fuseStorePath := filepath.Join(tmpDir, "fusestore.db")
|
||||
fuseManagerSocketPath := filepath.Join(tmpDir, "test-fusemanager.sock")
|
||||
|
||||
l, err := net.Listen("unix", socketPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
// Create server with mock
|
||||
grpcServer := grpc.NewServer()
|
||||
mockFs := newMockFileSystem(t)
|
||||
fm, err := newMockServer(context.Background(), l, grpcServer, fuseStorePath, fuseManagerSocketPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create fuse manager: %v", err)
|
||||
}
|
||||
defer fm.Close(context.Background())
|
||||
|
||||
pb.RegisterStargzFuseManagerServiceServer(grpcServer, fm)
|
||||
|
||||
// Set mock filesystem
|
||||
fm.curFs = mockFs
|
||||
|
||||
go grpcServer.Serve(l)
|
||||
defer grpcServer.Stop()
|
||||
|
||||
// Test cases to verify Init, Mount, Check and Unmount operations
|
||||
testCases := []struct {
|
||||
name string
|
||||
mountpoint string
|
||||
labels map[string]string
|
||||
initErr error
|
||||
mountErr error
|
||||
checkErr error
|
||||
unmountErr error
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "successful init and mount",
|
||||
mountpoint: filepath.Join(tmpDir, "mount1"),
|
||||
labels: map[string]string{"key": "value"},
|
||||
},
|
||||
{
|
||||
name: "init error",
|
||||
mountpoint: filepath.Join(tmpDir, "mount2"),
|
||||
initErr: fmt.Errorf("init error"),
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "mount error",
|
||||
mountpoint: filepath.Join(tmpDir, "mount3"),
|
||||
mountErr: fmt.Errorf("mount error"),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockFs.mountErr = tc.mountErr
|
||||
mockFs.checkErr = tc.checkErr
|
||||
mockFs.unmountErr = tc.unmountErr
|
||||
mockFs.mountCalled = false
|
||||
mockFs.checkCalled = false
|
||||
mockFs.unmountCalled = false
|
||||
fm.initErr = tc.initErr
|
||||
fm.initCalled = false
|
||||
|
||||
config := &Config{
|
||||
Config: service.Config{},
|
||||
}
|
||||
client, err := NewManagerClient(context.Background(), tmpDir, socketPath, config)
|
||||
if err != nil {
|
||||
if !tc.wantErr {
|
||||
t.Fatalf("failed to create client: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !fm.initCalled {
|
||||
t.Error("Init() was not called")
|
||||
}
|
||||
|
||||
if !tc.wantErr {
|
||||
// Test Mount
|
||||
err = client.Mount(context.Background(), tc.mountpoint, tc.labels)
|
||||
if err != nil {
|
||||
t.Errorf("Mount() error = %v", err)
|
||||
}
|
||||
if !mockFs.mountCalled {
|
||||
t.Error("Mount() was not called on filesystem")
|
||||
}
|
||||
|
||||
// Test Check
|
||||
err = client.Check(context.Background(), tc.mountpoint, tc.labels)
|
||||
if err != nil {
|
||||
t.Errorf("Check() error = %v", err)
|
||||
}
|
||||
if !mockFs.checkCalled {
|
||||
t.Error("Check() was not called on filesystem")
|
||||
}
|
||||
|
||||
// Test Unmount
|
||||
err = client.Unmount(context.Background(), tc.mountpoint)
|
||||
if err != nil {
|
||||
t.Errorf("Unmount() error = %v", err)
|
||||
}
|
||||
if !mockFs.unmountCalled {
|
||||
t.Error("Unmount() was not called on filesystem")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fusemanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
bolt "go.etcd.io/bbolt"
|
||||
|
||||
"github.com/containerd/stargz-snapshotter/service"
|
||||
)
|
||||
|
||||
var (
|
||||
fuseInfoBucket = []byte("fuse-info-bucket")
|
||||
)
|
||||
|
||||
type fuseInfo struct {
|
||||
Root string
|
||||
Mountpoint string
|
||||
Labels map[string]string
|
||||
Config service.Config
|
||||
}
|
||||
|
||||
func (fm *Server) storeFuseInfo(fuseInfo *fuseInfo) error {
|
||||
return fm.ms.Update(func(tx *bolt.Tx) error {
|
||||
bucket, err := tx.CreateBucketIfNotExists(fuseInfoBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key := []byte(fuseInfo.Mountpoint)
|
||||
|
||||
val, err := json.Marshal(fuseInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bucket.Put(key, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (fm *Server) removeFuseInfo(fuseInfo *fuseInfo) error {
|
||||
return fm.ms.Update(func(tx *bolt.Tx) error {
|
||||
bucket, err := tx.CreateBucketIfNotExists(fuseInfoBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key := []byte(fuseInfo.Mountpoint)
|
||||
|
||||
err = bucket.Delete(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// restoreFuseInfo restores fuseInfo when Init is called, it will skip mounted
|
||||
// layers whose mountpoint can be found in fsMap
|
||||
func (fm *Server) restoreFuseInfo(ctx context.Context) error {
|
||||
return fm.ms.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(fuseInfoBucket)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return bucket.ForEach(func(_, v []byte) error {
|
||||
mi := &fuseInfo{}
|
||||
err := json.Unmarshal(v, mi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fm.mount(ctx, mi.Mountpoint, mi.Labels)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fusemanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/moby/sys/mountinfo"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
pb "github.com/containerd/stargz-snapshotter/fusemanager/api"
|
||||
"github.com/containerd/stargz-snapshotter/service"
|
||||
"github.com/containerd/stargz-snapshotter/snapshot"
|
||||
)
|
||||
|
||||
const (
|
||||
FuseManagerNotReady = iota
|
||||
FuseManagerWaitInit
|
||||
FuseManagerReady
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Config service.Config
|
||||
IPFS bool `toml:"ipfs" json:"ipfs"`
|
||||
MetadataStore string `toml:"metadata_store" default:"memory" json:"metadata_store"`
|
||||
DefaultImageServiceAddress string `json:"default_image_service_address"`
|
||||
}
|
||||
|
||||
type ConfigContext struct {
|
||||
Ctx context.Context
|
||||
Config *Config
|
||||
RootDir string
|
||||
Server *grpc.Server
|
||||
OpenBoltDB func(string) (*bolt.DB, error)
|
||||
Address string
|
||||
CRIServer *grpc.Server
|
||||
}
|
||||
|
||||
var (
|
||||
configFuncs []ConfigFunc
|
||||
configMu sync.Mutex
|
||||
)
|
||||
|
||||
type ConfigFunc func(cc *ConfigContext) ([]service.Option, error)
|
||||
|
||||
func RegisterConfigFunc(f ConfigFunc) {
|
||||
configMu.Lock()
|
||||
defer configMu.Unlock()
|
||||
configFuncs = append(configFuncs, f)
|
||||
}
|
||||
|
||||
// Opens bolt DB with avoiding opening the same DB multiple times
|
||||
type dbOpener struct {
|
||||
mu sync.Mutex
|
||||
handles map[string]*bolt.DB
|
||||
}
|
||||
|
||||
func (o *dbOpener) openBoltDB(p string) (*bolt.DB, error) {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
|
||||
if db, ok := o.handles[p]; ok && db != nil {
|
||||
// we opened it before. avoid trying to open this again.
|
||||
return db, nil
|
||||
}
|
||||
|
||||
db, err := bolt.Open(p, 0600, &bolt.Options{
|
||||
NoFreelistSync: true,
|
||||
InitialMmapSize: 64 * 1024 * 1024,
|
||||
FreelistType: bolt.FreelistMapType,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if o.handles == nil {
|
||||
o.handles = make(map[string]*bolt.DB)
|
||||
}
|
||||
o.handles[p] = db
|
||||
return db, nil
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
pb.UnimplementedStargzFuseManagerServiceServer
|
||||
|
||||
lock sync.RWMutex
|
||||
status int32
|
||||
|
||||
listener net.Listener
|
||||
server *grpc.Server
|
||||
|
||||
// root is the latest root passed from containerd-stargz-grpc
|
||||
root string
|
||||
// config is the latest config passed from containerd-stargz-grpc
|
||||
config *Config
|
||||
// fsMap maps mountpoint to its filesystem instance to ensure Mount/Check/Unmount
|
||||
// call the proper filesystem
|
||||
fsMap sync.Map
|
||||
// curFs is filesystem created by latest config
|
||||
curFs snapshot.FileSystem
|
||||
ms *bolt.DB
|
||||
|
||||
fuseStoreAddr string
|
||||
|
||||
dbOpener *dbOpener
|
||||
|
||||
serverAddr string
|
||||
|
||||
curCRIServer *grpc.Server
|
||||
}
|
||||
|
||||
func NewFuseManager(ctx context.Context, listener net.Listener, server *grpc.Server, fuseStoreAddr string, serverAddr string) (*Server, error) {
|
||||
if err := os.MkdirAll(filepath.Dir(fuseStoreAddr), 0700); err != nil {
|
||||
return nil, fmt.Errorf("failed to create directory %q: %w", filepath.Dir(fuseStoreAddr), err)
|
||||
}
|
||||
|
||||
db, err := bolt.Open(fuseStoreAddr, 0666, &bolt.Options{Timeout: 10 * time.Second, ReadOnly: false})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to configure fusestore: %w", err)
|
||||
}
|
||||
|
||||
fm := &Server{
|
||||
status: FuseManagerWaitInit,
|
||||
lock: sync.RWMutex{},
|
||||
fsMap: sync.Map{},
|
||||
ms: db,
|
||||
listener: listener,
|
||||
server: server,
|
||||
fuseStoreAddr: fuseStoreAddr,
|
||||
dbOpener: &dbOpener{},
|
||||
serverAddr: serverAddr,
|
||||
}
|
||||
|
||||
return fm, nil
|
||||
}
|
||||
|
||||
func (fm *Server) Status(ctx context.Context, _ *pb.StatusRequest) (*pb.StatusResponse, error) {
|
||||
fm.lock.RLock()
|
||||
defer fm.lock.RUnlock()
|
||||
|
||||
return &pb.StatusResponse{
|
||||
Status: fm.status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (fm *Server) Init(ctx context.Context, req *pb.InitRequest) (*pb.Response, error) {
|
||||
fm.lock.Lock()
|
||||
fm.status = FuseManagerWaitInit
|
||||
defer func() {
|
||||
fm.status = FuseManagerReady
|
||||
fm.lock.Unlock()
|
||||
}()
|
||||
|
||||
ctx = log.WithLogger(ctx, log.G(ctx))
|
||||
|
||||
config := &Config{}
|
||||
err := json.Unmarshal(req.Config, config)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to get config")
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
fm.root = req.Root
|
||||
fm.config = config
|
||||
|
||||
if fm.curCRIServer != nil {
|
||||
fm.curCRIServer.Stop()
|
||||
fm.curCRIServer = nil
|
||||
}
|
||||
|
||||
cc := &ConfigContext{
|
||||
Ctx: ctx,
|
||||
Config: fm.config,
|
||||
RootDir: fm.root,
|
||||
Server: fm.server,
|
||||
OpenBoltDB: fm.dbOpener.openBoltDB,
|
||||
Address: fm.serverAddr,
|
||||
}
|
||||
|
||||
var opts []service.Option
|
||||
for _, configFunc := range configFuncs {
|
||||
funcOpts, err := configFunc(cc)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to apply config function")
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
opts = append(opts, funcOpts...)
|
||||
}
|
||||
|
||||
fm.curCRIServer = cc.CRIServer
|
||||
|
||||
fs, err := service.NewFileSystem(ctx, fm.root, &fm.config.Config, opts...)
|
||||
if err != nil {
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
fm.curFs = fs
|
||||
|
||||
err = fm.restoreFuseInfo(ctx)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to restore fuse info")
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
|
||||
return &pb.Response{}, nil
|
||||
}
|
||||
|
||||
func (fm *Server) Mount(ctx context.Context, req *pb.MountRequest) (*pb.Response, error) {
|
||||
fm.lock.RLock()
|
||||
defer fm.lock.RUnlock()
|
||||
if fm.status != FuseManagerReady {
|
||||
return &pb.Response{}, fmt.Errorf("fuse manager not ready")
|
||||
}
|
||||
|
||||
ctx = log.WithLogger(ctx, log.G(ctx).WithField("mountpoint", req.Mountpoint))
|
||||
|
||||
err := fm.mount(ctx, req.Mountpoint, req.Labels)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to mount stargz")
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
|
||||
fm.storeFuseInfo(&fuseInfo{
|
||||
Root: fm.root,
|
||||
Mountpoint: req.Mountpoint,
|
||||
Labels: req.Labels,
|
||||
Config: fm.config.Config,
|
||||
})
|
||||
|
||||
return &pb.Response{}, nil
|
||||
}
|
||||
|
||||
func (fm *Server) Check(ctx context.Context, req *pb.CheckRequest) (*pb.Response, error) {
|
||||
fm.lock.RLock()
|
||||
defer fm.lock.RUnlock()
|
||||
if fm.status != FuseManagerReady {
|
||||
return &pb.Response{}, fmt.Errorf("fuse manager not ready")
|
||||
}
|
||||
|
||||
ctx = log.WithLogger(ctx, log.G(ctx).WithField("mountpoint", req.Mountpoint))
|
||||
|
||||
obj, found := fm.fsMap.Load(req.Mountpoint)
|
||||
if !found {
|
||||
err := fmt.Errorf("failed to find filesystem of mountpoint %s", req.Mountpoint)
|
||||
log.G(ctx).WithError(err).Errorf("failed to check filesystem")
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
|
||||
fs := obj.(snapshot.FileSystem)
|
||||
err := fs.Check(ctx, req.Mountpoint, req.Labels)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to check filesystem")
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
|
||||
return &pb.Response{}, nil
|
||||
}
|
||||
|
||||
func (fm *Server) Unmount(ctx context.Context, req *pb.UnmountRequest) (*pb.Response, error) {
|
||||
fm.lock.RLock()
|
||||
defer fm.lock.RUnlock()
|
||||
if fm.status != FuseManagerReady {
|
||||
return &pb.Response{}, fmt.Errorf("fuse manager not ready")
|
||||
}
|
||||
|
||||
ctx = log.WithLogger(ctx, log.G(ctx).WithField("mountpoint", req.Mountpoint))
|
||||
|
||||
obj, found := fm.fsMap.Load(req.Mountpoint)
|
||||
if !found {
|
||||
// check whether already unmounted
|
||||
mounts, err := mountinfo.GetMounts(func(info *mountinfo.Info) (skip, stop bool) {
|
||||
if info.Mountpoint == req.Mountpoint {
|
||||
return false, true
|
||||
}
|
||||
return true, false
|
||||
})
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to get mount info")
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
|
||||
if len(mounts) <= 0 {
|
||||
return &pb.Response{}, nil
|
||||
}
|
||||
err = fmt.Errorf("failed to find filesystem of mountpoint %s", req.Mountpoint)
|
||||
log.G(ctx).WithError(err).Errorf("failed to unmount filesystem")
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
|
||||
fs := obj.(snapshot.FileSystem)
|
||||
err := fs.Unmount(ctx, req.Mountpoint)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to unmount filesystem")
|
||||
return &pb.Response{}, err
|
||||
}
|
||||
|
||||
fm.fsMap.Delete(req.Mountpoint)
|
||||
fm.removeFuseInfo(&fuseInfo{
|
||||
Mountpoint: req.Mountpoint,
|
||||
})
|
||||
|
||||
return &pb.Response{}, nil
|
||||
}
|
||||
|
||||
func (fm *Server) Close(ctx context.Context) error {
|
||||
fm.lock.Lock()
|
||||
defer fm.lock.Unlock()
|
||||
fm.status = FuseManagerNotReady
|
||||
|
||||
err := fm.ms.Close()
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to close fusestore")
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Remove(fm.fuseStoreAddr); err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to remove fusestore file %s", fm.fuseStoreAddr)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fm *Server) mount(ctx context.Context, mountpoint string, labels map[string]string) error {
|
||||
// mountpoint in fsMap means layer is already mounted, skip it
|
||||
if _, found := fm.fsMap.Load(mountpoint); found {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := fm.curFs.Mount(ctx, mountpoint, labels)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Errorf("failed to mount stargz")
|
||||
return err
|
||||
}
|
||||
|
||||
fm.fsMap.Store(mountpoint, fm.curFs)
|
||||
return nil
|
||||
}
|
160
go.mod
160
go.mod
|
@ -1,130 +1,138 @@
|
|||
module github.com/containerd/stargz-snapshotter
|
||||
|
||||
go 1.22.7
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.2
|
||||
|
||||
require (
|
||||
github.com/containerd/console v1.0.4
|
||||
github.com/containerd/containerd/v2 v2.0.0
|
||||
github.com/containerd/continuity v0.4.4
|
||||
github.com/containerd/console v1.0.5
|
||||
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
|
||||
github.com/containerd/platforms v1.0.0-rc.0
|
||||
github.com/containerd/platforms v1.0.0-rc.1
|
||||
github.com/containerd/plugin v1.0.0
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.1
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.17.0
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/cli v27.3.1+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
|
||||
github.com/hanwen/go-fuse/v2 v2.6.3
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7
|
||||
github.com/klauspost/compress v1.17.11
|
||||
github.com/hanwen/go-fuse/v2 v2.8.0
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/moby/sys/mountinfo v0.7.2
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/opencontainers/runtime-spec v1.2.0
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/opencontainers/runtime-spec v1.2.1
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/rs/xid v1.6.0
|
||||
golang.org/x/sync v0.9.0
|
||||
golang.org/x/sys v0.26.0
|
||||
google.golang.org/grpc v1.68.0
|
||||
k8s.io/api v0.31.2
|
||||
k8s.io/apimachinery v0.31.2
|
||||
k8s.io/client-go v0.31.2
|
||||
k8s.io/cri-api v0.32.0-alpha.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.74.2
|
||||
k8s.io/api v0.33.3
|
||||
k8s.io/apimachinery v0.33.3
|
||||
k8s.io/client-go v0.33.3
|
||||
k8s.io/cri-api v0.33.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.12.9 // indirect
|
||||
github.com/Microsoft/hcsshim v0.13.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.3 // indirect
|
||||
github.com/containerd/containerd/api v1.8.0 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.5 // indirect
|
||||
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.10 // indirect
|
||||
github.com/containerd/ttrpc v1.2.6 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.2 // indirect
|
||||
github.com/containernetworking/cni v1.2.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.7.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.7.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.19.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.1 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
|
||||
github.com/opencontainers/selinux v1.11.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/opencontainers/selinux v1.12.0 // indirect
|
||||
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.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/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.5 // indirect
|
||||
github.com/vbatts/tar-split v0.11.6 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.5 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.7 // indirect
|
||||
github.com/vbatts/tar-split v0.12.1 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
go.etcd.io/bbolt v1.3.11 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
|
||||
go.opentelemetry.io/otel v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.31.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/term v0.25.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // 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.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-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
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.0 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
tags.cncf.io/container-device-interface v0.8.0 // indirect
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0 // indirect
|
||||
)
|
||||
|
||||
// Import local package for estargz.
|
||||
replace github.com/containerd/stargz-snapshotter/estargz => ./estargz
|
||||
|
||||
exclude (
|
||||
// These dependencies were updated to "master" in some modules we depend on,
|
||||
// but have no code-changes since their last release. Unfortunately, this also
|
||||
// causes a ripple effect, forcing all users of the containerd module to also
|
||||
// update these dependencies to an unrelease / un-tagged version.
|
||||
//
|
||||
// Both these dependencies will unlikely do a new release in the near future,
|
||||
// so exclude these versions so that we can downgrade to the current release.
|
||||
//
|
||||
// For additional details, see this PR and links mentioned in that PR:
|
||||
// https://github.com/kubernetes-sigs/kustomize/pull/5830#issuecomment-2569960859
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
|
||||
)
|
||||
|
|
319
go.sum
319
go.sum
|
@ -1,67 +1,62 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||
github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
|
||||
github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
|
||||
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
|
||||
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
||||
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
|
||||
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
|
||||
github.com/containerd/containerd/v2 v2.0.0 h1:qLDdFaAykQrIyLiqwQrNLLz95wiC36bAZVwioUwqShM=
|
||||
github.com/containerd/containerd/v2 v2.0.0/go.mod h1:j25kDy9P48/ngb1sxWIFfK6GsnqOHoSqo1EpAod20VQ=
|
||||
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
|
||||
github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
||||
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
||||
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.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=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
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.10 h1:c2U73nld7spSWfiJwSh/8W9DK+/qQwYM2rngIhCyhyg=
|
||||
github.com/containerd/go-cni v1.1.10/go.mod h1:/Y/sL8yqYQn1ZG1om1OncJB1W4zN3YmjfP/ShCzG/OY=
|
||||
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.0 h1:GuHWSKgVVO3POn6nRBB4sH63uPOLa87yuuhsGLWaXAA=
|
||||
github.com/containerd/platforms v1.0.0-rc.0/go.mod h1:T1XAzzOdYs3it7l073MNXyxRwQofJfqwi/8cRjufIk4=
|
||||
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
|
||||
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
|
||||
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
|
||||
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
|
||||
github.com/containerd/ttrpc v1.2.6 h1:zG+Kn5EZ6MUYCS1t2Hmt2J4tMVaLSFEJVOraDQwNPC4=
|
||||
github.com/containerd/ttrpc v1.2.6/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.2 h1:3jN/k2ysKuPCsln5Qv8bzR9cxal8XjkxPogJfSNO31k=
|
||||
github.com/containerd/typeurl/v2 v2.2.2/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/containernetworking/cni v1.2.3 h1:hhOcjNVUQTnzdRJ6alC5XF+wd9mfGIUaj8FuJbEslXM=
|
||||
github.com/containernetworking/cni v1.2.3/go.mod h1:DuLgF+aPd3DzcTQTtp/Nvl1Kim23oFKdm2okJzBQA5M=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo=
|
||||
github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
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 v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ=
|
||||
github.com/docker/cli v27.3.1+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=
|
||||
|
@ -76,25 +71,24 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
|||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
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 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
|
@ -118,8 +112,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
|||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
@ -127,32 +121,22 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hanwen/go-fuse/v2 v2.6.3 h1:tDcEkLRx93lXu4XyN1/j8Z74VWvhHDl6qU1kNnvFUqI=
|
||||
github.com/hanwen/go-fuse/v2 v2.6.3/go.mod h1:ugNaD/iv5JYyS1Rcvi57Wz7/vrLQJo10mmketmoef48=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hanwen/go-fuse/v2 v2.8.0 h1:wV8rG7rmCz8XHSOwBZhG5YcVqcYjkzivjmbaMafPlAs=
|
||||
github.com/hanwen/go-fuse/v2 v2.8.0/go.mod h1:yE6D2PqWwm3CbYRxFXV9xUd8Md5d6NG0WBs5spCswmI=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
|
@ -162,8 +146,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
|||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
|
@ -182,7 +166,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||
|
@ -191,8 +174,8 @@ github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7z
|
|||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
|
||||
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -202,65 +185,62 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI=
|
||||
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
||||
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
|
||||
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
|
||||
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
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=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
|
||||
github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
|
@ -273,42 +253,40 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
|
||||
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
|
||||
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
|
||||
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
|
||||
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
||||
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
|
||||
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
|
||||
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
|
||||
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
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.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=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
@ -319,8 +297,6 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
|
|||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -332,40 +308,38 @@ 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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
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.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
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=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
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.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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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=
|
||||
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=
|
||||
|
@ -374,8 +348,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
|||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -385,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-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
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.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
|
||||
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
|
||||
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=
|
||||
|
@ -403,47 +377,44 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
||||
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
|
||||
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
|
||||
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
|
||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
|
||||
k8s.io/cri-api v0.32.0-alpha.0 h1:Rs9prajcHWZAdy9ueQdD2R+OOnDD3rKYbM9hQ90iEQU=
|
||||
k8s.io/cri-api v0.32.0-alpha.0/go.mod h1:Po3TMAYH/+KrZabi7QiwQI4a692oZcUOUThd/rqwxrI=
|
||||
k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8=
|
||||
k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE=
|
||||
k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA=
|
||||
k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA=
|
||||
k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg=
|
||||
k8s.io/cri-api v0.33.3 h1:aQvK3UxsaVMul4z71lOiblMHdhw9ROaw3Cgg15xDrD4=
|
||||
k8s.io/cri-api v0.33.3/go.mod h1:OLQvT45OpIA+tv91ZrpuFIGY+Y2Ho23poS7n115Aocs=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
|
||||
tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=
|
||||
|
|
72
ipfs/go.mod
72
ipfs/go.mod
|
@ -1,47 +1,46 @@
|
|||
module github.com/containerd/stargz-snapshotter/ipfs
|
||||
|
||||
go 1.22.7
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
github.com/containerd/containerd/v2 v2.0.0
|
||||
github.com/containerd/platforms v1.0.0-rc.0
|
||||
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.13.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/multiformats/go-multiaddr v0.16.1
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.12.9 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.3 // indirect
|
||||
github.com/containerd/containerd/api v1.8.0 // indirect
|
||||
github.com/containerd/continuity v0.4.4 // indirect
|
||||
github.com/Microsoft/hcsshim v0.13.0 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.5 // indirect
|
||||
github.com/containerd/containerd/api v1.9.0 // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/plugin v1.0.0 // indirect
|
||||
github.com/containerd/ttrpc v1.2.6 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.2 // indirect
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
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/fsnotify/fsnotify v1.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/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/ipfs/go-cid v0.0.7 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.2 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.1 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||
|
@ -50,30 +49,25 @@ require (
|
|||
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||
github.com/multiformats/go-varint v0.0.7 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.2.0 // indirect
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
|
||||
github.com/opencontainers/selinux v1.11.1 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.2.1 // indirect
|
||||
github.com/opencontainers/selinux v1.12.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
|
||||
go.opentelemetry.io/otel v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.31.0 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/grpc v1.68.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // 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.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.40.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.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
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
tags.cncf.io/container-device-interface v0.8.0 // indirect
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0 // indirect
|
||||
)
|
||||
|
|
174
ipfs/go.sum
174
ipfs/go.sum
|
@ -1,26 +1,22 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
|
||||
github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
|
||||
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
|
||||
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
|
||||
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
|
||||
github.com/containerd/containerd/v2 v2.0.0 h1:qLDdFaAykQrIyLiqwQrNLLz95wiC36bAZVwioUwqShM=
|
||||
github.com/containerd/containerd/v2 v2.0.0/go.mod h1:j25kDy9P48/ngb1sxWIFfK6GsnqOHoSqo1EpAod20VQ=
|
||||
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
|
||||
github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
||||
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.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=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
|
@ -29,18 +25,17 @@ github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY
|
|||
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
|
||||
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.0 h1:GuHWSKgVVO3POn6nRBB4sH63uPOLa87yuuhsGLWaXAA=
|
||||
github.com/containerd/platforms v1.0.0-rc.0/go.mod h1:T1XAzzOdYs3it7l073MNXyxRwQofJfqwi/8cRjufIk4=
|
||||
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
|
||||
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
|
||||
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
|
||||
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
|
||||
github.com/containerd/ttrpc v1.2.6 h1:zG+Kn5EZ6MUYCS1t2Hmt2J4tMVaLSFEJVOraDQwNPC4=
|
||||
github.com/containerd/ttrpc v1.2.6/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.2 h1:3jN/k2ysKuPCsln5Qv8bzR9cxal8XjkxPogJfSNO31k=
|
||||
github.com/containerd/typeurl/v2 v2.2.2/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
@ -49,11 +44,9 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
|||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
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=
|
||||
|
@ -80,21 +73,17 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY=
|
||||
github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
|
@ -103,7 +92,6 @@ github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dz
|
|||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||
|
@ -112,23 +100,22 @@ github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7z
|
|||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
|
||||
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
|
||||
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
||||
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||
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.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ=
|
||||
github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII=
|
||||
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=
|
||||
|
@ -140,16 +127,12 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n
|
|||
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI=
|
||||
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
||||
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
|
||||
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
|
||||
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -158,7 +141,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
|
|||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
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/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
|
@ -166,52 +148,44 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
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.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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
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-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4=
|
||||
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
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=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -221,30 +195,28 @@ 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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
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=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
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=
|
||||
|
@ -262,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-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
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.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
|
||||
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
|
||||
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=
|
||||
|
@ -280,11 +252,9 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -292,9 +262,3 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
|
||||
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
|
||||
tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -18,6 +18,7 @@ package testutil
|
|||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -25,13 +26,11 @@ import (
|
|||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/stargz-snapshotter/estargz"
|
||||
"github.com/containerd/stargz-snapshotter/metadata"
|
||||
tutil "github.com/containerd/stargz-snapshotter/util/testutil"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
@ -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 {
|
||||
|
@ -412,21 +442,21 @@ func newCalledTelemetry() (telemetry *metadata.Telemetry, check func() error) {
|
|||
GetTocLatency: func(time.Time) { getTocLatencyCalled = true },
|
||||
DeserializeTocLatency: func(time.Time) { deserializeTocLatencyCalled = true },
|
||||
}, func() error {
|
||||
var allErr error
|
||||
var errs []error
|
||||
if !getFooterLatencyCalled {
|
||||
allErr = multierror.Append(allErr, fmt.Errorf("metrics GetFooterLatency isn't called"))
|
||||
errs = append(errs, fmt.Errorf("metrics GetFooterLatency isn't called"))
|
||||
}
|
||||
if !getTocLatencyCalled {
|
||||
allErr = multierror.Append(allErr, fmt.Errorf("metrics GetTocLatency isn't called"))
|
||||
errs = append(errs, fmt.Errorf("metrics GetTocLatency isn't called"))
|
||||
}
|
||||
if !deserializeTocLatencyCalled {
|
||||
allErr = multierror.Append(allErr, fmt.Errorf("metrics DeserializeTocLatency isn't called"))
|
||||
errs = append(errs, fmt.Errorf("metrics DeserializeTocLatency isn't called"))
|
||||
}
|
||||
return allErr
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -109,11 +109,6 @@ func LayerConvertFuncWithCompressionLevel(compressionLevel zstd.EncoderLevel, op
|
|||
if uncompressedDesc == nil {
|
||||
return nil, fmt.Errorf("unexpectedly got the same blob after compression (%s, %q)", desc.Digest, desc.MediaType)
|
||||
}
|
||||
defer func() {
|
||||
if err := cs.Delete(ctx, uncompressedDesc.Digest); err != nil {
|
||||
log.G(ctx).WithError(err).WithField("uncompressedDesc", uncompressedDesc).Warn("failed to remove tmp uncompressed layer")
|
||||
}
|
||||
}()
|
||||
log.G(ctx).Debugf("zstdchunked: uncompressed %s into %s", desc.Digest, uncompressedDesc.Digest)
|
||||
}
|
||||
|
||||
|
@ -146,7 +141,7 @@ func LayerConvertFuncWithCompressionLevel(compressionLevel zstd.EncoderLevel, op
|
|||
}
|
||||
defer blob.Close()
|
||||
ref := fmt.Sprintf("convert-zstdchunked-from-%s", desc.Digest)
|
||||
w, err := cs.Writer(ctx, content.WithRef(ref))
|
||||
w, err := content.OpenWriter(ctx, cs, content.WithRef(ref))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -4,3 +4,5 @@ version = 2
|
|||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
|
|
|
@ -55,7 +55,7 @@ function kill_all {
|
|||
|
||||
function cleanup {
|
||||
rm -rf "${CONTAINERD_ROOT}"*
|
||||
if [ -f "${REMOTE_SNAPSHOTTER_SOCKET}" ] ; then
|
||||
if [ -e "${REMOTE_SNAPSHOTTER_SOCKET}" ] ; then
|
||||
rm "${REMOTE_SNAPSHOTTER_SOCKET}"
|
||||
fi
|
||||
if [ -d "${REMOTE_SNAPSHOTTER_ROOT}snapshotter/snapshots/" ] ; then
|
||||
|
|
|
@ -22,3 +22,5 @@ version = 2
|
|||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
version = 2
|
||||
|
||||
# Basic CRI configuration with enabling Stargz Snapshotter
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
default_runtime_name = "runc"
|
||||
snapshotter = "stargz"
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
|
||||
# Enable Stargz Snapshotter in Transfer Service
|
||||
[[plugins."io.containerd.transfer.v1.local".unpack_config]]
|
||||
platform = "linux"
|
||||
snapshotter = "stargz"
|
||||
# Enable overlayfs in Transfer Service
|
||||
[[plugins."io.containerd.transfer.v1.local".unpack_config]]
|
||||
platform = "linux"
|
||||
snapshotter = "overlayfs"
|
||||
|
||||
# Plugin Stargz Snapshotter
|
||||
[proxy_plugins]
|
||||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
enable_remote_snapshot_annotations = "true"
|
|
@ -19,7 +19,10 @@ set -euo pipefail
|
|||
CONTEXT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/"
|
||||
REPO="${CONTEXT}../../"
|
||||
CONTAINERD_SOCK=unix:///run/containerd/containerd.sock
|
||||
SNAPSHOTTER_SOCK=unix:///run/containerd-stargz-grpc/containerd-stargz-grpc.sock
|
||||
IMAGE_ENDPOINT_SOCK=unix:///run/containerd-stargz-grpc/containerd-stargz-grpc.sock
|
||||
if [ "${FUSE_MANAGER:-}" == "true" ] ; then
|
||||
IMAGE_ENDPOINT_SOCK=unix:///run/containerd-stargz-grpc/cri.sock
|
||||
fi
|
||||
|
||||
source "${CONTEXT}/const.sh"
|
||||
source "${REPO}/script/util/utils.sh"
|
||||
|
@ -62,7 +65,11 @@ if [ "${FAIL}" == "" ] ; then
|
|||
docker exec "${TEST_NODE_ID}" runc --version && \
|
||||
docker exec "${TEST_NODE_ID}" containerd --version && \
|
||||
echo "===============================" && \
|
||||
docker exec -i "${TEST_NODE_ID}" /go/bin/critest --runtime-endpoint=${CONTAINERD_SOCK} --image-endpoint=${SNAPSHOTTER_SOCK}
|
||||
# FIXME: remove the skip flag once kind adds support for the user namespace
|
||||
# See also https://github.com/kubernetes-sigs/kind/issues/3436
|
||||
docker exec -i "${TEST_NODE_ID}" /go/bin/critest \
|
||||
--runtime-endpoint=${CONTAINERD_SOCK} --image-endpoint=${IMAGE_ENDPOINT_SOCK} \
|
||||
--ginkgo.skip 'runtime should support NamespaceMode_POD'
|
||||
) ; then
|
||||
FAIL=true
|
||||
fi
|
||||
|
|
|
@ -22,7 +22,10 @@ REPO="${CONTEXT}../../"
|
|||
REGISTRY_HOST="cri-registry"
|
||||
TEST_NODE_NAME="cri-testenv-container"
|
||||
CONTAINERD_SOCK=unix:///run/containerd/containerd.sock
|
||||
SNAPSHOTTER_SOCK=unix:///run/containerd-stargz-grpc/containerd-stargz-grpc.sock
|
||||
IMAGE_ENDPOINT_SOCK=unix:///run/containerd-stargz-grpc/containerd-stargz-grpc.sock
|
||||
if [ "${FUSE_MANAGER:-}" == "true" ] ; then
|
||||
IMAGE_ENDPOINT_SOCK=unix:///run/containerd-stargz-grpc/cri.sock
|
||||
fi
|
||||
PREPARE_NODE_NAME="cri-prepare-node"
|
||||
|
||||
source "${CONTEXT}/const.sh"
|
||||
|
@ -115,11 +118,16 @@ docker exec "${PREPARE_NODE_NAME}" /bin/bash /tools/mirror.sh
|
|||
# Configure mirror registries for containerd and snapshotter
|
||||
docker exec "${TEST_NODE_NAME}" cat /etc/containerd/config.toml > "${CONTAINERD_CONFIG}"
|
||||
docker exec "${TEST_NODE_NAME}" cat /etc/containerd-stargz-grpc/config.toml > "${SNAPSHOTTER_CONFIG}"
|
||||
docker exec "${TEST_NODE_NAME}" mkdir -p "/etc/containerd/certs.d"
|
||||
cat <<EOF >> "${CONTAINERD_CONFIG}"
|
||||
[plugins."io.containerd.cri.v1.images".registry]
|
||||
config_path = "/etc/containerd/certs.d"
|
||||
EOF
|
||||
cat "${IMAGE_LIST}" | sed -E 's/^([^/]*).*/\1/g' | sort | uniq | while read DOMAIN ; do
|
||||
echo "Adding mirror config: ${DOMAIN}"
|
||||
cat <<EOF >> "${CONTAINERD_CONFIG}"
|
||||
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."${DOMAIN}"]
|
||||
endpoint = ["http://${REGISTRY_HOST}:5000"]
|
||||
docker exec "${TEST_NODE_NAME}" mkdir -p "/etc/containerd/certs.d/${DOMAIN}/"
|
||||
cat <<EOF | docker exec -i "${TEST_NODE_NAME}" tee -a "/etc/containerd/certs.d/${DOMAIN}/hosts.toml"
|
||||
server = "http://${REGISTRY_HOST}:5000"
|
||||
EOF
|
||||
if [ "${BUILTIN_SNAPSHOTTER:-}" == "true" ] ; then
|
||||
cat <<EOF >> "${CONTAINERD_CONFIG}"
|
||||
|
@ -182,7 +190,12 @@ echo "===== VERSION INFORMATION ====="
|
|||
docker exec "${TEST_NODE_NAME}" runc --version
|
||||
docker exec "${TEST_NODE_NAME}" containerd --version
|
||||
echo "==============================="
|
||||
docker exec "${TEST_NODE_NAME}" /go/bin/critest --runtime-endpoint=${CONTAINERD_SOCK} --image-endpoint=${SNAPSHOTTER_SOCK}
|
||||
|
||||
# FIXME: remove the skip flag once kind adds support for the user namespace
|
||||
# See also https://github.com/kubernetes-sigs/kind/issues/3436
|
||||
docker exec "${TEST_NODE_NAME}" /go/bin/critest \
|
||||
--runtime-endpoint=${CONTAINERD_SOCK} --image-endpoint=${IMAGE_ENDPOINT_SOCK} \
|
||||
--ginkgo.skip 'runtime should support NamespaceMode_POD'
|
||||
|
||||
echo "Check if stargz snapshotter is working"
|
||||
docker exec "${TEST_NODE_NAME}" \
|
||||
|
|
|
@ -50,7 +50,7 @@ function cleanup {
|
|||
}
|
||||
trap 'cleanup "$?"' EXIT SIGHUP SIGINT SIGQUIT SIGTERM
|
||||
|
||||
BUILTIN_HACK_INST=
|
||||
ADDITIONAL_INST=
|
||||
if [ "${BUILTIN_SNAPSHOTTER:-}" == "true" ] ; then
|
||||
# Special configuration for CRI containerd + builtin stargz snapshotter
|
||||
cat <<EOF > "${TMP_CONTEXT}/containerd.hack.toml"
|
||||
|
@ -75,7 +75,7 @@ metadata_store = "memory"
|
|||
[plugins."io.containerd.snapshotter.v1.stargz".cri_keychain]
|
||||
enable_keychain = true
|
||||
EOF
|
||||
BUILTIN_HACK_INST="COPY containerd.hack.toml /etc/containerd/config.toml"
|
||||
ADDITIONAL_INST="COPY containerd.hack.toml /etc/containerd/config.toml"
|
||||
fi
|
||||
|
||||
cat <<EOF > "${TMP_CONTEXT}/test.conflist"
|
||||
|
@ -114,11 +114,33 @@ if [ "${METADATA_STORE:-}" != "" ] ; then
|
|||
USE_METADATA_STORE="${METADATA_STORE}"
|
||||
fi
|
||||
|
||||
FUSE_MANAGER_CONFIG=""
|
||||
if [ "${FUSE_MANAGER:-}" == "true" ] ; then
|
||||
FUSE_MANAGER_CONFIG='listen_path = "/run/containerd-stargz-grpc/cri.sock"
|
||||
[fuse_manager]
|
||||
enable = true'
|
||||
fi
|
||||
|
||||
SNAPSHOTTER_CONFIG_FILE=/etc/containerd-stargz-grpc/config.toml
|
||||
if [ "${BUILTIN_SNAPSHOTTER:-}" == "true" ] ; then
|
||||
SNAPSHOTTER_CONFIG_FILE=/etc/containerd/config.toml
|
||||
fi
|
||||
|
||||
USE_FUSE_PASSTHROUGH="false"
|
||||
if [ "${FUSE_PASSTHROUGH:-}" != "" ] ; then
|
||||
USE_FUSE_PASSTHROUGH="${FUSE_PASSTHROUGH}"
|
||||
if [ "${BUILTIN_SNAPSHOTTER:-}" == "true" ] && [ "${FUSE_PASSTHROUGH}" == "true" ] ; then
|
||||
echo "builtin snapshotter + fuse passthrough test is unsupported"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${TRANSFER_SERVICE:-}" == "true" ] ; then
|
||||
cp "${CONTEXT}/config.containerd.transfer.toml" "${TMP_CONTEXT}/"
|
||||
ADDITIONAL_INST="${ADDITIONAL_INST}
|
||||
COPY config.containerd.transfer.toml /etc/containerd/config.toml"
|
||||
fi
|
||||
|
||||
# Prepare the testing node
|
||||
cat <<EOF > "${TMP_CONTEXT}/Dockerfile"
|
||||
# Legacy builder that doesn't support TARGETARCH should set this explicitly using --build-arg.
|
||||
|
@ -131,7 +153,7 @@ ENV PATH=$PATH:/usr/local/go/bin
|
|||
ENV GOPATH=/go
|
||||
# Do not install git and its dependencies here which will cause failure of building the image
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends make && \
|
||||
curl -Ls https://dl.google.com/go/go1.23.0.linux-\${TARGETARCH:-amd64}.tar.gz | tar -C /usr/local -xz && \
|
||||
curl -Ls https://dl.google.com/go/go1.24.0.linux-\${TARGETARCH:-amd64}.tar.gz | tar -C /usr/local -xz && \
|
||||
go install github.com/onsi/ginkgo/ginkgo@${GINKGO_VERSION} && \
|
||||
mkdir -p \${GOPATH}/src/github.com/kubernetes-sigs/cri-tools /tmp/cri-tools && \
|
||||
curl -sL https://github.com/kubernetes-sigs/cri-tools/archive/refs/tags/v${CRI_TOOLS_VERSION}.tar.gz | tar -C /tmp/cri-tools -xz && \
|
||||
|
@ -143,9 +165,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends make && \
|
|||
|
||||
COPY ./test.conflist /etc/cni/net.d/test.conflist
|
||||
|
||||
${BUILTIN_HACK_INST}
|
||||
${ADDITIONAL_INST}
|
||||
|
||||
RUN sed -i 's/^metadata_store.*/metadata_store = "${USE_METADATA_STORE}"/g' "${SNAPSHOTTER_CONFIG_FILE}"
|
||||
RUN <<EEE
|
||||
cat <<EOT >> "${SNAPSHOTTER_CONFIG_FILE}"
|
||||
${FUSE_MANAGER_CONFIG}
|
||||
EOT
|
||||
EEE
|
||||
|
||||
RUN if [ "${BUILTIN_SNAPSHOTTER:-}" != "true" ] ; then \
|
||||
sed -i '1imetadata_store = "${USE_METADATA_STORE}"' "${SNAPSHOTTER_CONFIG_FILE}" && \
|
||||
echo '[fuse]' >> "${SNAPSHOTTER_CONFIG_FILE}" && \
|
||||
echo "passthrough = ${USE_FUSE_PASSTHROUGH}" >> "${SNAPSHOTTER_CONFIG_FILE}" ; \
|
||||
fi
|
||||
|
||||
ENTRYPOINT [ "/usr/local/bin/entrypoint", "/sbin/init" ]
|
||||
EOF
|
||||
|
|
|
@ -66,7 +66,7 @@ ENV PATH=$PATH:/usr/local/go/bin
|
|||
ENV GOPATH=/go
|
||||
# Do not install git and its dependencies here which will cause failure of building the image
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends make && \
|
||||
curl -Ls https://dl.google.com/go/go1.23.0.linux-\${TARGETARCH:-amd64}.tar.gz | tar -C /usr/local -xz && \
|
||||
curl -Ls https://dl.google.com/go/go1.24.0.linux-\${TARGETARCH:-amd64}.tar.gz | tar -C /usr/local -xz && \
|
||||
go install github.com/onsi/ginkgo/ginkgo@${GINKGO_VERSION} && \
|
||||
mkdir -p \${GOPATH}/src/github.com/kubernetes-sigs/cri-tools /tmp/cri-tools && \
|
||||
curl -sL https://github.com/kubernetes-sigs/cri-tools/archive/refs/tags/v${CRI_TOOLS_VERSION}.tar.gz | tar -C /tmp/cri-tools -xz && \
|
||||
|
|
|
@ -4,3 +4,5 @@ version = 2
|
|||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
|
|
|
@ -46,13 +46,14 @@ function retry {
|
|||
|
||||
function kill_all {
|
||||
if [ "${1}" != "" ] ; then
|
||||
ps aux | grep "${1}" | grep -v grep | sed -E 's/ +/ /g' | cut -f 2 -d ' ' | xargs -I{} kill -9 {} || true
|
||||
S="${2:-SIGKILL}"
|
||||
ps aux | grep "${1} " | grep -v grep | sed -E 's/ +/ /g' | cut -f 2 -d ' ' | xargs -I{} kill -s $S {} || true
|
||||
fi
|
||||
}
|
||||
|
||||
function cleanup {
|
||||
rm -rf "${CONTAINERD_ROOT}"*
|
||||
if [ -f "${REMOTE_SNAPSHOTTER_SOCKET}" ] ; then
|
||||
if [ -e "${REMOTE_SNAPSHOTTER_SOCKET}" ] ; then
|
||||
rm "${REMOTE_SNAPSHOTTER_SOCKET}"
|
||||
fi
|
||||
if [ -d "${REMOTE_SNAPSHOTTER_ROOT}snapshotter/snapshots/" ] ; then
|
||||
|
@ -71,6 +72,7 @@ mkdir -p "${CONTAINERD_CONFIG_DIR}" "${REMOTE_SNAPSHOTTER_CONFIG_DIR}" "${CNI_CO
|
|||
echo "cleaning up the environment..."
|
||||
kill_all "containerd"
|
||||
kill_all "containerd-stargz-grpc"
|
||||
kill_all "stargz-fuse-manager" SIGTERM
|
||||
cleanup
|
||||
|
||||
echo "preparing commands..."
|
||||
|
|
|
@ -10,3 +10,13 @@ check_always = true
|
|||
|
||||
[plugins."io.containerd.snapshotter.v1.stargz".registry.mirrors."registry-integration.test"]
|
||||
endpoint = ["http://registry-alt.test:5000"]
|
||||
|
||||
# Enable Stargz Snapshotter in Transfer Service
|
||||
[[plugins."io.containerd.transfer.v1.local".unpack_config]]
|
||||
platform = "linux"
|
||||
snapshotter = "stargz"
|
||||
|
||||
# Enable overlayfs in Transfer Service
|
||||
[[plugins."io.containerd.transfer.v1.local".unpack_config]]
|
||||
platform = "linux"
|
||||
snapshotter = "overlayfs"
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
metadata_store = "memory"
|
||||
ipfs = true
|
||||
|
||||
[fuse]
|
||||
passthrough = false
|
||||
|
||||
[blob]
|
||||
check_always = true
|
||||
max_retries = 1
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
set -eux -o pipefail
|
||||
|
||||
# NOTE: The entire contents of containerd/stargz-snapshotter are located in
|
||||
# the testing container so utils.sh is visible from this script during runtime.
|
||||
|
@ -28,6 +28,8 @@ REGISTRY_ALT_HOST=registry-alt.test
|
|||
DUMMYUSER=dummyuser
|
||||
DUMMYPASS=dummypass
|
||||
|
||||
FUSE_MANAGER_LOG="Start snapshotter with fusemanager mode"
|
||||
|
||||
USR_ORG=$(mktemp -d)
|
||||
USR_MIRROR=$(mktemp -d)
|
||||
USR_REFRESH=$(mktemp -d)
|
||||
|
@ -93,22 +95,32 @@ function retry {
|
|||
|
||||
function kill_all {
|
||||
if [ "${1}" != "" ] ; then
|
||||
ps aux | grep "${1}" | grep -v grep | grep -v $(basename ${0}) | sed -E 's/ +/ /g' | cut -f 2 -d ' ' | xargs -I{} kill -9 {} || true
|
||||
TARGET_SIGNAL="-s ${2:-SIGKILL}"
|
||||
ps aux | grep "${1} " | grep -v grep | grep -v $(basename ${0}) | sed -E 's/ +/ /g' | cut -f 2 -d ' ' | xargs -I{} kill ${TARGET_SIGNAL} {} || true
|
||||
fi
|
||||
}
|
||||
|
||||
function wait_all {
|
||||
while ps aux | grep -v grep | grep -v $(basename ${0}) | grep "${1} " ; do sleep 3 ; done
|
||||
}
|
||||
|
||||
CONTAINERD_ROOT=/var/lib/containerd/
|
||||
CONTAINERD_STATUS=/run/containerd/
|
||||
REMOTE_SNAPSHOTTER_SOCKET=/run/containerd-stargz-grpc/containerd-stargz-grpc.sock
|
||||
REMOTE_SNAPSHOTTER_ROOT=/var/lib/containerd-stargz-grpc/
|
||||
REMOTE_SNAPSHOTTER_FUSE_MANAGER_SOCKET=/run/containerd-stargz-grpc/fuse-manager.sock
|
||||
function reboot_containerd {
|
||||
kill_all "containerd"
|
||||
kill_all "containerd-stargz-grpc"
|
||||
kill_all "stargz-fuse-manager" SIGTERM
|
||||
rm -rf "${CONTAINERD_STATUS}"*
|
||||
rm -rf "${CONTAINERD_ROOT}"*
|
||||
if [ -f "${REMOTE_SNAPSHOTTER_SOCKET}" ] ; then
|
||||
if [ -e "${REMOTE_SNAPSHOTTER_SOCKET}" ] ; then
|
||||
rm "${REMOTE_SNAPSHOTTER_SOCKET}"
|
||||
fi
|
||||
if [ -e "${REMOTE_SNAPSHOTTER_FUSE_MANAGER_SOCKET}" ] ; then
|
||||
rm "${REMOTE_SNAPSHOTTER_FUSE_MANAGER_SOCKET}"
|
||||
fi
|
||||
if [ -d "${REMOTE_SNAPSHOTTER_ROOT}snapshotter/snapshots/" ] ; then
|
||||
find "${REMOTE_SNAPSHOTTER_ROOT}snapshotter/snapshots/" \
|
||||
-maxdepth 1 -mindepth 1 -type d -exec umount "{}/fs" \;
|
||||
|
@ -133,6 +145,19 @@ function reboot_containerd {
|
|||
fi
|
||||
retry ls "${REMOTE_SNAPSHOTTER_SOCKET}"
|
||||
containerd --log-level debug --config=/etc/containerd/config.toml &
|
||||
if [ "${NO_FUSE_MANAGER_CHECK:-}" != "true" ] ; then
|
||||
if cat "${LOG_FILE}" | grep "${FUSE_MANAGER_LOG}" ; then
|
||||
if [ "${FUSE_MANAGER}" != "true" ] ; then
|
||||
echo "fuse manager should not be enabled"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [ "${FUSE_MANAGER}" == "true" ] ; then
|
||||
echo "fuse manager should be enabled"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Makes sure containerd and containerd-stargz-grpc are up-and-running.
|
||||
|
@ -171,9 +196,9 @@ function convert {
|
|||
function copy {
|
||||
local SRC="${1}"
|
||||
local DST="${2}"
|
||||
ctr-remote i pull --all-platforms "${SRC}"
|
||||
ctr-remote i tag "${SRC}" "${DST}"
|
||||
ctr-remote i push -u "${DUMMYUSER}:${DUMMYPASS}" "${DST}"
|
||||
ctr-remote image pull --all-platforms "${SRC}"
|
||||
ctr-remote image tag "${SRC}" "${DST}"
|
||||
ctr-remote image push -u "${DUMMYUSER}:${DUMMYPASS}" "${DST}"
|
||||
}
|
||||
|
||||
function copy_out_dir {
|
||||
|
@ -191,6 +216,11 @@ function copy_out_dir {
|
|||
rm "${TMPFILE}"
|
||||
}
|
||||
|
||||
RPULL_COMMAND="rpull"
|
||||
if [ "${USE_TRANSFER_SERVICE}" == "true" ] ; then
|
||||
RPULL_COMMAND="pull --snapshotter=stargz"
|
||||
fi
|
||||
|
||||
function dump_dir {
|
||||
local IMAGE="${1}"
|
||||
local TARGETDIR="${2}"
|
||||
|
@ -200,9 +230,9 @@ function dump_dir {
|
|||
|
||||
reboot_containerd
|
||||
if [ "${REMOTE}" == "true" ] ; then
|
||||
run_and_check_remote_snapshots ctr-remote images rpull --user "${DUMMYUSER}:${DUMMYPASS}" "${IMAGE}"
|
||||
run_and_check_remote_snapshots ctr-remote images ${RPULL_COMMAND} --user "${DUMMYUSER}:${DUMMYPASS}" "${IMAGE}"
|
||||
else
|
||||
ctr-remote images pull --snapshotter="${SNAPSHOTTER}" --user "${DUMMYUSER}:${DUMMYPASS}" "${IMAGE}"
|
||||
ctr-remote image pull --snapshotter="${SNAPSHOTTER}" --user "${DUMMYUSER}:${DUMMYPASS}" "${IMAGE}"
|
||||
fi
|
||||
copy_out_dir "${IMAGE}" "${TARGETDIR}" "${DEST}" "${SNAPSHOTTER}"
|
||||
}
|
||||
|
@ -243,6 +273,9 @@ if [ "${BUILTIN_SNAPSHOTTER}" != "true" ] ; then
|
|||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
|
||||
[proxy_plugins.stargz.exports]
|
||||
root = "/var/lib/containerd-stargz-grpc/"
|
||||
enable_remote_snapshot_annotations = "true"
|
||||
EOF
|
||||
fi
|
||||
|
||||
|
@ -276,9 +309,9 @@ optimize "${REGISTRY_HOST}/alpine:3.15.3" "${REGISTRY_ALT_HOST}:5000/alpine:esgz
|
|||
# TODO: support external TOC suffix other than "-esgztoc"
|
||||
optimize "${REGISTRY_HOST}/ubuntu:22.04" "${REGISTRY_HOST}/ubuntu:esgz-50000" "false" --estargz-min-chunk-size=50000
|
||||
optimize "${REGISTRY_HOST}/ubuntu:22.04" "${REGISTRY_HOST}/ubuntu:esgz-ex" "false" --estargz-external-toc
|
||||
ctr-remote i push -u "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:esgz-ex-esgztoc"
|
||||
ctr-remote image push -u "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:esgz-ex-esgztoc"
|
||||
convert "${REGISTRY_HOST}/ubuntu:22.04" "${REGISTRY_HOST}/ubuntu:esgz-ex-keep-diff-id" "false" --estargz --estargz-external-toc --estargz-keep-diff-id
|
||||
ctr-remote i push -u "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:esgz-ex-keep-diff-id-esgztoc"
|
||||
ctr-remote image push -u "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:esgz-ex-keep-diff-id-esgztoc"
|
||||
|
||||
if [ "${BUILTIN_SNAPSHOTTER}" != "true" ] ; then
|
||||
|
||||
|
@ -294,21 +327,21 @@ if [ "${BUILTIN_SNAPSHOTTER}" != "true" ] ; then
|
|||
retry curl -X POST localhost:5001/api/v0/version >/dev/null 2>&1 # wait for up
|
||||
|
||||
# stargz snapshotter (default labels)
|
||||
ctr-remote i pull --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:22.04"
|
||||
ctr-remote image pull --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:22.04"
|
||||
CID=$(ctr-remote i ipfs-push "${REGISTRY_HOST}/ubuntu:22.04")
|
||||
reboot_containerd
|
||||
run_and_check_remote_snapshots ctr-remote i rpull --ipfs "${CID}"
|
||||
copy_out_dir "${CID}" "/usr" "${USR_STARGZSN_IPFS}" "stargz"
|
||||
|
||||
# stargz snapshotter (containerd labels)
|
||||
ctr-remote i pull --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:22.04"
|
||||
ctr-remote image pull --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:22.04"
|
||||
CID=$(ctr-remote i ipfs-push "${REGISTRY_HOST}/ubuntu:22.04")
|
||||
reboot_containerd
|
||||
run_and_check_remote_snapshots ctr-remote i rpull --use-containerd-labels --ipfs "${CID}"
|
||||
copy_out_dir "${CID}" "/usr" "${USR_STARGZSN_CTD_IPFS}" "stargz"
|
||||
|
||||
# overlayfs snapshotter
|
||||
ctr-remote i pull --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:22.04"
|
||||
ctr-remote image pull --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/ubuntu:22.04"
|
||||
CID=$(ctr-remote i ipfs-push --estargz=false "${REGISTRY_HOST}/ubuntu:22.04")
|
||||
reboot_containerd
|
||||
ctr-remote i rpull --snapshotter=overlayfs --ipfs "${CID}"
|
||||
|
@ -329,11 +362,11 @@ echo "Testing refreshing and mirror..."
|
|||
|
||||
reboot_containerd
|
||||
echo "Getting image with normal snapshotter..."
|
||||
ctr-remote images pull --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/alpine:esgz"
|
||||
ctr-remote image pull --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/alpine:esgz"
|
||||
copy_out_dir "${REGISTRY_HOST}/alpine:esgz" "/usr" "${USR_ORG}" "overlayfs"
|
||||
|
||||
echo "Getting image with stargz snapshotter..."
|
||||
run_and_check_remote_snapshots ctr-remote images rpull --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/alpine:esgz"
|
||||
run_and_check_remote_snapshots ctr-remote images ${RPULL_COMMAND} --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/alpine:esgz"
|
||||
|
||||
REGISTRY_HOST_IP=$(getent hosts "${REGISTRY_HOST}" | awk '{ print $1 }')
|
||||
REGISTRY_ALT_HOST_IP=$(getent hosts "${REGISTRY_ALT_HOST}" | awk '{ print $1 }')
|
||||
|
@ -473,13 +506,62 @@ diff --no-dereference -qr "${USR_NORMALSN_PLAIN_STARGZ}/" "${USR_STARGZSN_PLAIN_
|
|||
|
||||
############
|
||||
# Try to pull this image from different namespace.
|
||||
ctr-remote --namespace=dummy images rpull --user "${DUMMYUSER}:${DUMMYPASS}" \
|
||||
ctr-remote --namespace=dummy images ${RPULL_COMMAND} --user "${DUMMYUSER}:${DUMMYPASS}" \
|
||||
"${REGISTRY_HOST}/ubuntu:esgz"
|
||||
|
||||
############
|
||||
# Test for starting when no configuration file.
|
||||
mv /etc/containerd-stargz-grpc/config.toml /etc/containerd-stargz-grpc/config.toml_rm
|
||||
reboot_containerd
|
||||
NO_FUSE_MANAGER_CHECK=true reboot_containerd
|
||||
mv /etc/containerd-stargz-grpc/config.toml_rm /etc/containerd-stargz-grpc/config.toml
|
||||
|
||||
############
|
||||
# Test graceful restart
|
||||
|
||||
function check_cache_empty {
|
||||
TARGET_DIRS=("${REMOTE_SNAPSHOTTER_ROOT}stargz/httpcache/" "${REMOTE_SNAPSHOTTER_ROOT}stargz/fscache/")
|
||||
for D in "${TARGET_DIRS[@]}" ; do
|
||||
test -z "$( ls -A $D )"
|
||||
done
|
||||
}
|
||||
|
||||
reboot_containerd
|
||||
if [ "${BUILTIN_SNAPSHOTTER}" != "true" ] ; then
|
||||
# Snapshots should be available even after restarting the snapshotter with a signal
|
||||
run_and_check_remote_snapshots ctr-remote images ${RPULL_COMMAND} --user "${DUMMYUSER}:${DUMMYPASS}" "${REGISTRY_HOST}/alpine:esgz"
|
||||
ctr-remote run --rm --snapshotter=stargz "${REGISTRY_HOST}/alpine:esgz" test echo hi
|
||||
|
||||
TARGET_SIGNALS=(SIGINT SIGTERM)
|
||||
for S in "${TARGET_SIGNALS[@]}" ; do
|
||||
# Kill the snapshotter
|
||||
kill_all containerd-stargz-grpc "$S"
|
||||
|
||||
# wait until stargz snapshotter is finished
|
||||
wait_all containerd-stargz-grpc
|
||||
|
||||
if [ "$S" == "SIGINT" ] ; then
|
||||
# On SIGINT, fuse manager mode also performs graceful shutdown so test this behaviour.
|
||||
if [ "${FUSE_MANAGER}" == "true" ] ; then
|
||||
kill_all stargz-fuse-manager "$S"
|
||||
wait_all stargz-fuse-manager
|
||||
fi
|
||||
# Check if resource are cleaned up
|
||||
check_cache_empty
|
||||
else
|
||||
if [ "${FUSE_MANAGER}" != "true" ] ; then
|
||||
# If this is not FUSE manager mode, check if resource are cleaned up
|
||||
check_cache_empty
|
||||
fi
|
||||
fi
|
||||
|
||||
# Restart the snapshotter without additional operation
|
||||
containerd-stargz-grpc --log-level=debug --address="${REMOTE_SNAPSHOTTER_SOCKET}" 2>&1 | tee -a "${LOG_FILE}" & # Dump all log
|
||||
retry nc -z -U "${REMOTE_SNAPSHOTTER_SOCKET}"
|
||||
sleep 3 # FIXME: Additional wait; sometimes snapshotter is still unavailable after the socket ready
|
||||
|
||||
# Check if the snapshotter is still usable
|
||||
ctr-remote run --rm --snapshotter=stargz "${REGISTRY_HOST}/alpine:esgz" test echo hi
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -55,11 +55,31 @@ if [ "${METADATA_STORE:-}" != "" ] ; then
|
|||
USE_METADATA_STORE="${METADATA_STORE}"
|
||||
fi
|
||||
|
||||
FUSE_MANAGER_CONFIG=""
|
||||
if [ "${FUSE_MANAGER:-}" == "true" ] ; then
|
||||
FUSE_MANAGER_CONFIG="[fuse_manager]
|
||||
enable = true"
|
||||
fi
|
||||
|
||||
SNAPSHOTTER_CONFIG_FILE=/etc/containerd-stargz-grpc/config.toml
|
||||
if [ "${BUILTIN_SNAPSHOTTER:-}" == "true" ] ; then
|
||||
SNAPSHOTTER_CONFIG_FILE=/etc/containerd/config.toml
|
||||
fi
|
||||
|
||||
USE_FUSE_PASSTHROUGH="false"
|
||||
if [ "${FUSE_PASSTHROUGH:-}" != "" ] ; then
|
||||
USE_FUSE_PASSTHROUGH="${FUSE_PASSTHROUGH}"
|
||||
if [ "${BUILTIN_SNAPSHOTTER:-}" == "true" ] && [ "${FUSE_PASSTHROUGH}" == "true" ] ; then
|
||||
echo "builtin snapshotter + fuse passthrough test is unsupported"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
USE_TRANSFER_SERVICE=false
|
||||
if [ "${TRANSFER_SERVICE:-}" == "true" ] ; then
|
||||
USE_TRANSFER_SERVICE=true
|
||||
fi
|
||||
|
||||
DOCKER_COMPOSE_YAML=$(mktemp)
|
||||
AUTH_DIR=$(mktemp -d)
|
||||
SS_ROOT_DIR=$(mktemp -d)
|
||||
|
@ -81,7 +101,7 @@ cat <<EOF > "${TMP_CONTEXT}/Dockerfile"
|
|||
FROM ${INTEGRATION_BASE_IMAGE_NAME}
|
||||
|
||||
RUN apt-get update -y && \
|
||||
apt-get --no-install-recommends install -y iptables jq && \
|
||||
apt-get --no-install-recommends install -y iptables jq netcat && \
|
||||
go install github.com/google/crfs/stargz/stargzify@71d77da419c90be7b05d12e59945ac7a8c94a543 && \
|
||||
wget https://dist.ipfs.io/go-ipfs/${IPFS_VERSION}/go-ipfs_${IPFS_VERSION}_linux-amd64.tar.gz && \
|
||||
tar -xvzf go-ipfs_${IPFS_VERSION}_linux-amd64.tar.gz && \
|
||||
|
@ -92,10 +112,19 @@ COPY ./containerd/config.containerd.toml /etc/containerd/config.toml
|
|||
COPY ./containerd/config.stargz.toml /etc/containerd-stargz-grpc/config.toml
|
||||
COPY ./containerd/entrypoint.sh ./utils.sh /
|
||||
|
||||
RUN sed -i 's/^metadata_store.*/metadata_store = "${USE_METADATA_STORE}"/g' "${SNAPSHOTTER_CONFIG_FILE}"
|
||||
RUN if [ "${BUILTIN_SNAPSHOTTER:-}" != "true" ] ; then \
|
||||
sed -i 's/^metadata_store.*/metadata_store = "${USE_METADATA_STORE}"/g' "${SNAPSHOTTER_CONFIG_FILE}" && \
|
||||
sed -i 's/^passthrough.*/passthrough = ${USE_FUSE_PASSTHROUGH}/g' "${SNAPSHOTTER_CONFIG_FILE}" ; \
|
||||
fi
|
||||
|
||||
RUN <<EEE
|
||||
cat <<EOT >> "${SNAPSHOTTER_CONFIG_FILE}"
|
||||
${FUSE_MANAGER_CONFIG}
|
||||
EOT
|
||||
EEE
|
||||
|
||||
ENV CONTAINERD_SNAPSHOTTER=""
|
||||
|
||||
ENV USE_TRANSFER_SERVICE="${USE_TRANSFER_SERVICE}"
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
EOF
|
||||
docker build ${DOCKER_BUILD_ARGS:-} -t "${INTEGRATION_TEST_IMAGE_NAME}" ${DOCKER_BUILD_ARGS:-} "${TMP_CONTEXT}"
|
||||
|
@ -118,6 +147,7 @@ services:
|
|||
- http_proxy=${http_proxy:-}
|
||||
- https_proxy=${https_proxy:-}
|
||||
- BUILTIN_SNAPSHOTTER=${BUILTIN_SNAPSHOTTER:-}
|
||||
- FUSE_MANAGER=${FUSE_MANAGER:-}
|
||||
tmpfs:
|
||||
- /tmp:exec,mode=777
|
||||
volumes:
|
||||
|
|
|
@ -22,6 +22,7 @@ REPO="${CONTEXT}../../"
|
|||
K3S_VERSION=master
|
||||
K3S_REPO=https://github.com/k3s-io/k3s
|
||||
K3S_CONTAINERD_REPO=https://github.com/k3s-io/containerd
|
||||
ARGO_VERSION=v3.6.4
|
||||
|
||||
K3S_NODE_REPO=ghcr.io/stargz-containers
|
||||
K3S_NODE_IMAGE_NAME=k3s
|
||||
|
@ -86,7 +87,33 @@ function run {
|
|||
--k3s-arg='--snapshotter='"${SNAPSHOTTER}"'@server:*;agent:*'
|
||||
kubectl create ns argo
|
||||
kubectl apply -n argo -f "${CUSTOM_ARGOYAML}"
|
||||
|
||||
# workaround for argo failing to list namespaces
|
||||
cat <<EOF | kubectl -n argo apply -f -
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: argo-namespace-access-cr
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- namespaces
|
||||
verbs:
|
||||
- list
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: argo-namespace-access-crb
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: argo-namespace-access-cr
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: argo
|
||||
namespace: argo
|
||||
EOF
|
||||
# Wait for the cluster is ready
|
||||
local RETRYNUM=30
|
||||
local RETRYINTERVAL=1
|
||||
|
@ -122,7 +149,7 @@ if [ "${RESULT_FILE}" == "" ] ; then
|
|||
fi
|
||||
echo "result to ${RESULT_FILE}"
|
||||
|
||||
wget -O "${ORG_ARGOYAML}" https://raw.githubusercontent.com/argoproj/argo-workflows/v3.4.3/manifests/quick-start-minimal.yaml
|
||||
wget -O "${ORG_ARGOYAML}" https://raw.githubusercontent.com/argoproj/argo-workflows/${ARGO_VERSION}/manifests/quick-start-minimal.yaml
|
||||
|
||||
git clone -b ${K3S_VERSION} --depth 1 "${K3S_REPO}" "${TMP_K3S_REPO}"
|
||||
sed -i "s|github.com/k3s-io/stargz-snapshotter .*$|$(realpath ${REPO})|g" "${TMP_K3S_REPO}/go.mod"
|
||||
|
|
|
@ -204,9 +204,7 @@ echo "Checking optimized image..."
|
|||
WORKING_DIR=$(mktemp -d)
|
||||
git config --global --add safe.directory '/go/src/github.com/containerd/stargz-snapshotter'
|
||||
PREFIX=/tmp/out/ make clean
|
||||
# temporary disable -race flag to run test with statically built binaries
|
||||
# PREFIX=/tmp/out/ GO_BUILD_FLAGS="-race" make ctr-remote # Check data race
|
||||
PREFIX=/tmp/out/ make ctr-remote
|
||||
PREFIX=/tmp/out/ GO_BUILD_FLAGS="-race" make ctr-remote # Check data race
|
||||
/tmp/out/ctr-remote ${OPTIMIZE_COMMAND} -entrypoint='[ "/accessor" ]' "${ORG_IMAGE_TAG}" "${OPT_IMAGE_TAG}"
|
||||
nerdctl push "${OPT_IMAGE_TAG}" || true
|
||||
cat <<EOF > "${WORKING_DIR}/0-want"
|
||||
|
|
|
@ -26,35 +26,38 @@ type Config struct {
|
|||
config.Config
|
||||
|
||||
// KubeconfigKeychainConfig is config for kubeconfig-based keychain.
|
||||
KubeconfigKeychainConfig `toml:"kubeconfig_keychain"`
|
||||
KubeconfigKeychainConfig `toml:"kubeconfig_keychain" json:"kubeconfig_keychain"`
|
||||
|
||||
// CRIKeychainConfig is config for CRI-based keychain.
|
||||
CRIKeychainConfig `toml:"cri_keychain"`
|
||||
CRIKeychainConfig `toml:"cri_keychain" json:"cri_keychain"`
|
||||
|
||||
// ResolverConfig is config for resolving registries.
|
||||
ResolverConfig `toml:"resolver"`
|
||||
ResolverConfig `toml:"resolver" json:"resolver"`
|
||||
|
||||
// SnapshotterConfig is snapshotter-related config.
|
||||
SnapshotterConfig `toml:"snapshotter"`
|
||||
SnapshotterConfig `toml:"snapshotter" json:"snapshotter"`
|
||||
}
|
||||
|
||||
// KubeconfigKeychainConfig is config for kubeconfig-based keychain.
|
||||
type KubeconfigKeychainConfig struct {
|
||||
// EnableKeychain enables kubeconfig-based keychain
|
||||
EnableKeychain bool `toml:"enable_keychain"`
|
||||
EnableKeychain bool `toml:"enable_keychain" json:"enable_keychain"`
|
||||
|
||||
// KubeconfigPath is the path to kubeconfig which can be used to sync
|
||||
// secrets on the cluster into this snapshotter.
|
||||
KubeconfigPath string `toml:"kubeconfig_path"`
|
||||
KubeconfigPath string `toml:"kubeconfig_path" json:"kubeconfig_path"`
|
||||
}
|
||||
|
||||
// CRIKeychainConfig is config for CRI-based keychain.
|
||||
type CRIKeychainConfig struct {
|
||||
// EnableKeychain enables CRI-based keychain
|
||||
EnableKeychain bool `toml:"enable_keychain"`
|
||||
EnableKeychain bool `toml:"enable_keychain" json:"enable_keychain"`
|
||||
|
||||
// ImageServicePath is the path to the unix socket of backing CRI Image Service (e.g. containerd CRI plugin)
|
||||
ImageServicePath string `toml:"image_service_path"`
|
||||
ImageServicePath string `toml:"image_service_path" json:"image_service_path"`
|
||||
|
||||
// ListenPath is the path to the unix socket to listen
|
||||
ListenPath string `toml:"listen_path" json:"listen_path"`
|
||||
}
|
||||
|
||||
// ResolverConfig is config for resolving registries.
|
||||
|
@ -66,5 +69,5 @@ type SnapshotterConfig struct {
|
|||
// data source when restarting the snapshotter.
|
||||
// NOTE: User needs to manually remove the snapshots from containerd's metadata store using
|
||||
// ctr (e.g. `ctr snapshot rm`).
|
||||
AllowInvalidMountsOnRestart bool `toml:"allow_invalid_mounts_on_restart"`
|
||||
AllowInvalidMountsOnRestart bool `toml:"allow_invalid_mounts_on_restart" json:"allow_invalid_mounts_on_restart"`
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package keychainconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/defaults"
|
||||
"github.com/containerd/containerd/v2/pkg/dialer"
|
||||
"github.com/containerd/stargz-snapshotter/service/keychain/cri"
|
||||
"github.com/containerd/stargz-snapshotter/service/keychain/dockerconfig"
|
||||
"github.com/containerd/stargz-snapshotter/service/keychain/kubeconfig"
|
||||
"github.com/containerd/stargz-snapshotter/service/resolver"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
EnableKubeKeychain bool
|
||||
EnableCRIKeychain bool
|
||||
KubeconfigPath string
|
||||
DefaultImageServiceAddress string
|
||||
ImageServicePath string
|
||||
}
|
||||
|
||||
func ConfigKeychain(ctx context.Context, rpc *grpc.Server, config *Config) ([]resolver.Credential, error) {
|
||||
credsFuncs := []resolver.Credential{dockerconfig.NewDockerconfigKeychain(ctx)}
|
||||
if config.EnableKubeKeychain {
|
||||
var opts []kubeconfig.Option
|
||||
if kcp := config.KubeconfigPath; kcp != "" {
|
||||
opts = append(opts, kubeconfig.WithKubeconfigPath(kcp))
|
||||
}
|
||||
credsFuncs = append(credsFuncs, kubeconfig.NewKubeconfigKeychain(ctx, opts...))
|
||||
}
|
||||
if config.EnableCRIKeychain {
|
||||
// connects to the backend CRI service (defaults to containerd socket)
|
||||
criAddr := config.DefaultImageServiceAddress
|
||||
if cp := config.ImageServicePath; cp != "" {
|
||||
criAddr = cp
|
||||
}
|
||||
connectCRI := func() (runtime.ImageServiceClient, error) {
|
||||
conn, err := newCRIConn(criAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return runtime.NewImageServiceClient(conn), nil
|
||||
}
|
||||
f, criServer := cri.NewCRIKeychain(ctx, connectCRI)
|
||||
runtime.RegisterImageServiceServer(rpc, criServer)
|
||||
credsFuncs = append(credsFuncs, f)
|
||||
}
|
||||
|
||||
return credsFuncs, nil
|
||||
}
|
||||
|
||||
func newCRIConn(criAddr string) (*grpc.ClientConn, error) {
|
||||
// TODO: make gRPC options configurable from config.toml
|
||||
backoffConfig := backoff.DefaultConfig
|
||||
backoffConfig.MaxDelay = 3 * time.Second
|
||||
connParams := grpc.ConnectParams{
|
||||
Backoff: backoffConfig,
|
||||
}
|
||||
gopts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithConnectParams(connParams),
|
||||
grpc.WithContextDialer(dialer.ContextDialer),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
|
||||
}
|
||||
return grpc.NewClient(dialer.DialAddress(criAddr), gopts...)
|
||||
}
|
|
@ -146,7 +146,7 @@ func (kc *keychain) credentials(host string, refspec reference.Spec) (string, st
|
|||
if acfg, err := cfg.GetAuthConfig(host); err == nil {
|
||||
if acfg.IdentityToken != "" {
|
||||
return "", acfg.IdentityToken, nil
|
||||
} else if !(acfg.Username == "" && acfg.Password == "") {
|
||||
} else if acfg.Username != "" || acfg.Password != "" {
|
||||
return acfg.Username, acfg.Password, nil
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ func (kc *keychain) startSyncSecrets(ctx context.Context, client kubernetes.Inte
|
|||
})
|
||||
go informer.Run(ctx.Done())
|
||||
if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced) {
|
||||
return fmt.Errorf("Timed out for syncing cache")
|
||||
return fmt.Errorf("timed out for syncing cache")
|
||||
}
|
||||
|
||||
// get informer and queue
|
||||
|
|
|
@ -78,17 +78,17 @@ func RegisterPlugin() {
|
|||
|
||||
// Configure keychain
|
||||
credsFuncs := []resolver.Credential{dockerconfig.NewDockerconfigKeychain(ctx)}
|
||||
if config.Config.KubeconfigKeychainConfig.EnableKeychain {
|
||||
if config.KubeconfigKeychainConfig.EnableKeychain {
|
||||
var opts []kubeconfig.Option
|
||||
if kcp := config.Config.KubeconfigKeychainConfig.KubeconfigPath; kcp != "" {
|
||||
if kcp := config.KubeconfigPath; kcp != "" {
|
||||
opts = append(opts, kubeconfig.WithKubeconfigPath(kcp))
|
||||
}
|
||||
credsFuncs = append(credsFuncs, kubeconfig.NewKubeconfigKeychain(ctx, opts...))
|
||||
}
|
||||
if addr := config.CRIKeychainImageServicePath; config.Config.CRIKeychainConfig.EnableKeychain && addr != "" {
|
||||
if addr := config.CRIKeychainImageServicePath; config.CRIKeychainConfig.EnableKeychain && addr != "" {
|
||||
// connects to the backend CRI service (defaults to containerd socket)
|
||||
criAddr := ic.Properties[ctdplugins.PropertyGRPCAddress]
|
||||
if cp := config.Config.CRIKeychainConfig.ImageServicePath; cp != "" {
|
||||
if cp := config.ImageServicePath; cp != "" {
|
||||
criAddr = cp
|
||||
}
|
||||
if criAddr == "" {
|
||||
|
|
|
@ -31,28 +31,28 @@ const defaultRequestTimeoutSec = 30
|
|||
|
||||
// Config is config for resolving registries.
|
||||
type Config struct {
|
||||
Host map[string]HostConfig `toml:"host"`
|
||||
Host map[string]HostConfig `toml:"host" json:"host"`
|
||||
}
|
||||
|
||||
type HostConfig struct {
|
||||
Mirrors []MirrorConfig `toml:"mirrors"`
|
||||
Mirrors []MirrorConfig `toml:"mirrors" json:"mirrors"`
|
||||
}
|
||||
|
||||
type MirrorConfig struct {
|
||||
|
||||
// Host is the hostname of the host.
|
||||
Host string `toml:"host"`
|
||||
Host string `toml:"host" json:"host"`
|
||||
|
||||
// Insecure is true means use http scheme instead of https.
|
||||
Insecure bool `toml:"insecure"`
|
||||
Insecure bool `toml:"insecure" json:"insecure"`
|
||||
|
||||
// RequestTimeoutSec is timeout seconds of each request to the registry.
|
||||
// RequestTimeoutSec == 0 indicates the default timeout (defaultRequestTimeoutSec).
|
||||
// RequestTimeoutSec < 0 indicates no timeout.
|
||||
RequestTimeoutSec int `toml:"request_timeout_sec"`
|
||||
RequestTimeoutSec int `toml:"request_timeout_sec" json:"request_timeout_sec"`
|
||||
|
||||
// Header are additional headers to send to the server
|
||||
Header map[string]interface{} `toml:"header"`
|
||||
Header map[string]interface{} `toml:"header" json:"header"`
|
||||
}
|
||||
|
||||
type Credential func(string, reference.Spec) (string, string, error)
|
||||
|
@ -120,7 +120,7 @@ func multiCredsFuncs(ref reference.Spec, credsFuncs ...Credential) func(string)
|
|||
for _, f := range credsFuncs {
|
||||
if username, secret, err := f(host, ref); err != nil {
|
||||
return "", "", err
|
||||
} else if !(username == "" && secret == "") {
|
||||
} else if username != "" || secret != "" {
|
||||
return username, secret, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package service
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/snapshots"
|
||||
|
@ -30,8 +32,7 @@ import (
|
|||
"github.com/containerd/stargz-snapshotter/metadata"
|
||||
esgzexternaltoc "github.com/containerd/stargz-snapshotter/nativeconverter/estargz/externaltoc"
|
||||
"github.com/containerd/stargz-snapshotter/service/resolver"
|
||||
snbase "github.com/containerd/stargz-snapshotter/snapshot"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/containerd/stargz-snapshotter/snapshot"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
|
@ -66,6 +67,27 @@ func WithFilesystemOptions(opts ...stargzfs.Option) Option {
|
|||
|
||||
// NewStargzSnapshotterService returns stargz snapshotter.
|
||||
func NewStargzSnapshotterService(ctx context.Context, root string, config *Config, opts ...Option) (snapshots.Snapshotter, error) {
|
||||
fs, err := NewFileSystem(ctx, root, config, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to configure filesystem: %w", err)
|
||||
}
|
||||
|
||||
var snapshotter snapshots.Snapshotter
|
||||
|
||||
snOpts := []snapshot.Opt{snapshot.AsynchronousRemove}
|
||||
if config.AllowInvalidMountsOnRestart {
|
||||
snOpts = append(snOpts, snapshot.AllowInvalidMountsOnRestart)
|
||||
}
|
||||
|
||||
snapshotter, err = snapshot.NewSnapshotter(ctx, snapshotterRoot(root), fs, snOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create new snapshotter: %w", err)
|
||||
}
|
||||
|
||||
return snapshotter, nil
|
||||
}
|
||||
|
||||
func NewFileSystem(ctx context.Context, root string, config *Config, opts ...Option) (snapshot.FileSystem, error) {
|
||||
var sOpts options
|
||||
for _, o := range opts {
|
||||
o(&sOpts)
|
||||
|
@ -97,22 +119,10 @@ func NewStargzSnapshotterService(ctx context.Context, root string, config *Confi
|
|||
)
|
||||
fs, err := stargzfs.NewFilesystem(fsRoot(root), config.Config, fsOpts...)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to configure filesystem")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var snapshotter snapshots.Snapshotter
|
||||
|
||||
snOpts := []snbase.Opt{snbase.AsynchronousRemove}
|
||||
if config.SnapshotterConfig.AllowInvalidMountsOnRestart {
|
||||
snOpts = append(snOpts, snbase.AllowInvalidMountsOnRestart)
|
||||
}
|
||||
|
||||
snapshotter, err = snbase.NewSnapshotter(ctx, snapshotterRoot(root), fs, snOpts...)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Fatalf("failed to create new snapshotter")
|
||||
}
|
||||
|
||||
return snapshotter, err
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
func snapshotterRoot(root string) string {
|
||||
|
@ -125,14 +135,15 @@ func fsRoot(root string) string {
|
|||
|
||||
func sources(ps ...source.GetSources) source.GetSources {
|
||||
return func(labels map[string]string) (source []source.Source, allErr error) {
|
||||
var errs []error
|
||||
for _, p := range ps {
|
||||
src, err := p(labels)
|
||||
if err == nil {
|
||||
return src, nil
|
||||
}
|
||||
allErr = multierror.Append(allErr, err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ type snapshotter struct {
|
|||
// the root as same as overlayfs snapshotter.
|
||||
func NewSnapshotter(ctx context.Context, root string, targetFs FileSystem, opts ...Opt) (snapshots.Snapshotter, error) {
|
||||
if targetFs == nil {
|
||||
return nil, fmt.Errorf("Specify filesystem to use")
|
||||
return nil, fmt.Errorf("specify filesystem to use")
|
||||
}
|
||||
|
||||
var config SnapshotterConfig
|
||||
|
@ -658,6 +658,7 @@ func (o *snapshotter) Close() error {
|
|||
if err := o.cleanup(ctx, cleanupCommitted); err != nil {
|
||||
log.G(ctx).WithError(err).Warn("failed to cleanup")
|
||||
}
|
||||
|
||||
return o.ms.Close()
|
||||
}
|
||||
|
||||
|
@ -722,6 +723,10 @@ func (o *snapshotter) checkAvailability(ctx context.Context, key string) bool {
|
|||
}
|
||||
|
||||
func (o *snapshotter) restoreRemoteSnapshot(ctx context.Context) error {
|
||||
if o.noRestore {
|
||||
return nil
|
||||
}
|
||||
|
||||
mounts, err := mountinfo.GetMounts(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -734,10 +739,6 @@ func (o *snapshotter) restoreRemoteSnapshot(ctx context.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
if o.noRestore {
|
||||
return nil
|
||||
}
|
||||
|
||||
var task []snapshots.Info
|
||||
if err := o.Walk(ctx, func(ctx context.Context, info snapshots.Info) error {
|
||||
if _, ok := info.Labels[remoteLabel]; ok {
|
||||
|
@ -748,6 +749,27 @@ func (o *snapshotter) restoreRemoteSnapshot(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
for _, info := range task {
|
||||
// First, prepare the snapshot directory
|
||||
if err := func() error {
|
||||
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer t.Rollback()
|
||||
id, _, _, err := storage.GetInfo(ctx, info.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Mkdir(filepath.Join(o.root, "snapshots", id), 0700); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
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)
|
||||
}
|
||||
if err := o.prepareRemoteSnapshot(ctx, info.Name, info.Labels); err != nil {
|
||||
if o.allowInvalidMountsOnRestart {
|
||||
log.G(ctx).WithError(err).Warnf("failed to restore remote snapshot %s; remove this snapshot manually", info.Name)
|
||||
|
|
26
store/fs.go
26
store/fs.go
|
@ -56,7 +56,7 @@ const (
|
|||
)
|
||||
|
||||
func Mount(ctx context.Context, mountpoint string, layerManager *LayerManager, debug bool) error {
|
||||
timeSec := time.Second
|
||||
t := time.Second
|
||||
rawFS := fusefs.NewNodeFS(&rootnode{
|
||||
fs: &fs{
|
||||
layerManager: layerManager,
|
||||
|
@ -64,8 +64,8 @@ func Mount(ctx context.Context, mountpoint string, layerManager *LayerManager, d
|
|||
layerMap: new(idMap),
|
||||
},
|
||||
}, &fusefs.Options{
|
||||
AttrTimeout: &timeSec,
|
||||
EntryTimeout: &timeSec,
|
||||
AttrTimeout: &t,
|
||||
EntryTimeout: &t,
|
||||
NullPermissions: true,
|
||||
})
|
||||
mountOpts := &fuse.MountOptions{
|
||||
|
@ -135,7 +135,7 @@ func (n *rootnode) Lookup(ctx context.Context, name string, out *fuse.EntryOut)
|
|||
log.G(ctx).Warn("rootnode.Lookup: uknown node type detected")
|
||||
return nil, syscall.EIO
|
||||
}
|
||||
out.Attr.Ino = cn.StableAttr().Ino
|
||||
out.Ino = cn.StableAttr().Ino
|
||||
return cn, 0
|
||||
}
|
||||
switch name {
|
||||
|
@ -145,7 +145,7 @@ func (n *rootnode) Lookup(ctx context.Context, name string, out *fuse.EntryOut)
|
|||
cn := &MemSymlinkOnForget{fusefs.MemSymlink{Data: []byte(n.fs.layerManager.refPool.root())}, n.fs, fattr}
|
||||
copyAttr(cn.attr, &out.Attr)
|
||||
return n.fs.newInodeWithID(ctx, func(ino uint32) fusefs.InodeEmbedder {
|
||||
out.Attr.Ino = uint64(ino)
|
||||
out.Ino = uint64(ino)
|
||||
cn.attr.Ino = uint64(ino)
|
||||
sAttr.Ino = uint64(ino)
|
||||
fattr.Ino = uint64(ino)
|
||||
|
@ -170,7 +170,7 @@ func (n *rootnode) Lookup(ctx context.Context, name string, out *fuse.EntryOut)
|
|||
}
|
||||
copyAttr(&cn.attr, &out.Attr)
|
||||
return n.fs.newInodeWithID(ctx, func(ino uint32) fusefs.InodeEmbedder {
|
||||
out.Attr.Ino = uint64(ino)
|
||||
out.Ino = uint64(ino)
|
||||
cn.attr.Ino = uint64(ino)
|
||||
sAttr.Ino = uint64(ino)
|
||||
return n.NewPersistentInode(ctx, cn, sAttr)
|
||||
|
@ -207,7 +207,7 @@ func (n *refnode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (
|
|||
log.G(ctx).Warn("refnode.Lookup: uknown node type detected")
|
||||
return nil, syscall.EIO
|
||||
}
|
||||
out.Attr.Ino = cn.StableAttr().Ino
|
||||
out.Ino = cn.StableAttr().Ino
|
||||
return cn, 0
|
||||
}
|
||||
targetDigest, err := digest.Parse(name)
|
||||
|
@ -223,7 +223,7 @@ func (n *refnode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (
|
|||
}
|
||||
copyAttr(&cn.attr, &out.Attr)
|
||||
return n.fs.newInodeWithID(ctx, func(ino uint32) fusefs.InodeEmbedder {
|
||||
out.Attr.Ino = uint64(ino)
|
||||
out.Ino = uint64(ino)
|
||||
cn.attr.Ino = uint64(ino)
|
||||
sAttr.Ino = uint64(ino)
|
||||
return n.NewPersistentInode(ctx, cn, sAttr)
|
||||
|
@ -321,7 +321,7 @@ func (n *layernode) Lookup(ctx context.Context, name string, out *fuse.EntryOut)
|
|||
log.G(ctx).Warn("layernode.Lookup: uknown node type detected")
|
||||
return nil, syscall.EIO
|
||||
}
|
||||
out.Attr.Ino = cn.StableAttr().Ino
|
||||
out.Ino = cn.StableAttr().Ino
|
||||
return cn, 0
|
||||
}
|
||||
switch name {
|
||||
|
@ -342,7 +342,7 @@ func (n *layernode) Lookup(ctx context.Context, name string, out *fuse.EntryOut)
|
|||
cn := &MemRegularFileOnForget{fusefs.MemRegularFile{Data: infoData}, n.fs, &fattr}
|
||||
copyAttr(cn.attr, &out.Attr)
|
||||
return n.fs.newInodeWithID(ctx, func(ino uint32) fusefs.InodeEmbedder {
|
||||
out.Attr.Ino = uint64(ino)
|
||||
out.Ino = uint64(ino)
|
||||
cn.attr.Ino = uint64(ino)
|
||||
sAttr.Ino = uint64(ino)
|
||||
fattr.Ino = uint64(ino)
|
||||
|
@ -381,7 +381,7 @@ func (n *layernode) Lookup(ctx context.Context, name string, out *fuse.EntryOut)
|
|||
cn := &blobnode{l: l, fs: n.fs}
|
||||
copyAttr(&cn.attr, &out.Attr)
|
||||
return n.fs.newInodeWithID(ctx, func(ino uint32) fusefs.InodeEmbedder {
|
||||
out.Attr.Ino = uint64(ino)
|
||||
out.Ino = uint64(ino)
|
||||
cn.attr.Ino = uint64(ino)
|
||||
sAttr.Ino = uint64(ino)
|
||||
return n.NewPersistentInode(ctx, cn, sAttr)
|
||||
|
@ -408,8 +408,8 @@ func (n *layernode) Lookup(ctx context.Context, name string, out *fuse.EntryOut)
|
|||
|
||||
copyAttr(&out.Attr, &ao.Attr)
|
||||
cn = n.NewPersistentInode(ctx, root, fusefs.StableAttr{
|
||||
Mode: out.Attr.Mode,
|
||||
Ino: out.Attr.Ino,
|
||||
Mode: out.Mode,
|
||||
Ino: out.Ino,
|
||||
})
|
||||
return nil
|
||||
}()
|
||||
|
|
|
@ -100,10 +100,7 @@ func (ts *BackgroundTaskManager) DonePrioritizedTask() {
|
|||
func (ts *BackgroundTaskManager) InvokeBackgroundTask(do func(context.Context), timeout time.Duration) {
|
||||
for {
|
||||
// Wait until all prioritized tasks are done
|
||||
for {
|
||||
if atomic.LoadInt64(&ts.prioritizedTasks) <= 0 {
|
||||
break
|
||||
}
|
||||
for atomic.LoadInt64(&ts.prioritizedTasks) > 0 {
|
||||
|
||||
// waits until a prioritized task is done
|
||||
ts.prioritizedTaskDoneCond.L.Lock()
|
||||
|
|
|
@ -44,7 +44,7 @@ func NewTTLCache(ttl time.Duration) *TTLCache {
|
|||
// Get retrieves the specified object from the cache and increments the reference counter of the
|
||||
// target content. Client must call `done` callback to decrease the reference count when the value
|
||||
// will no longer be used.
|
||||
func (c *TTLCache) Get(key string) (value interface{}, done func(), ok bool) {
|
||||
func (c *TTLCache) Get(key string) (value interface{}, done func(bool), ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
rc, ok := c.m[key]
|
||||
|
@ -59,7 +59,7 @@ func (c *TTLCache) Get(key string) (value interface{}, done func(), ok bool) {
|
|||
// If the specified content already exists in the cache, this sets `added` to false and returns
|
||||
// "already cached" content (i.e. doesn't replace the content with the new one). Client must call
|
||||
// `done` callback to decrease the counter when the value will no longer be used.
|
||||
func (c *TTLCache) Add(key string, value interface{}) (cachedValue interface{}, done func(), added bool) {
|
||||
func (c *TTLCache) Add(key string, value interface{}) (cachedValue interface{}, done func(bool), added bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if rc, ok := c.m[key]; ok {
|
||||
|
@ -100,12 +100,20 @@ func (c *TTLCache) evictLocked(key string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *TTLCache) decreaseOnceFunc(rc *refCounterWithTimer) func() {
|
||||
func (c *TTLCache) decreaseOnceFunc(rc *refCounterWithTimer) func(bool) {
|
||||
var once sync.Once
|
||||
return func() {
|
||||
return func(evict bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
once.Do(func() { rc.dec() })
|
||||
if evict {
|
||||
rc.t.Stop()
|
||||
rc.finalize()
|
||||
key := rc.key
|
||||
if cachedRc, ok := c.m[key]; ok && cachedRc == rc {
|
||||
delete(c.m, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,12 +78,12 @@ func TestTTLRemove(t *testing.T) {
|
|||
t.Fatalf("no content must be evicted after remove")
|
||||
}
|
||||
|
||||
done1()
|
||||
done1(false)
|
||||
if len(evicted) != 0 {
|
||||
t.Fatalf("no content must be evicted until all reference are discarded")
|
||||
}
|
||||
|
||||
done12()
|
||||
done12(false)
|
||||
if len(evicted) != 1 {
|
||||
t.Fatalf("content must be evicted")
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ func TestTTLRemoveOverwritten(t *testing.T) {
|
|||
}
|
||||
key1, value1 := "key1", "abcd1"
|
||||
_, done1, _ := c.Add(key1, value1)
|
||||
done1()
|
||||
done1(false)
|
||||
c.Remove(key1) // remove key1 as soon as possible
|
||||
|
||||
// add another content with a new key
|
||||
|
@ -120,8 +120,8 @@ func TestTTLRemoveOverwritten(t *testing.T) {
|
|||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
done122()
|
||||
done12()
|
||||
done122(false)
|
||||
done12(false)
|
||||
// spent 4 sec since the new key1 was added. This should be expierd.
|
||||
if _, _, ok := c.Get(key1); ok {
|
||||
t.Fatalf("%q must be expierd but remaining", key1)
|
||||
|
@ -143,7 +143,7 @@ func TestTTLEviction(t *testing.T) {
|
|||
key1, value1 := "key1", "abcd1"
|
||||
key2, value2 := "key2", "abcd2"
|
||||
_, done1, _ := c.Add(key1, value1)
|
||||
done1() // evict key1 on expiering ttl
|
||||
done1(false) // evict key1 on expiering ttl
|
||||
_, done2, _ := c.Add(key2, value2)
|
||||
_, done22, _ := c.Get(key2) // hold reference of key2 to prevent eviction
|
||||
time.Sleep(3 * time.Second) // wait until elements reach ttl
|
||||
|
@ -157,16 +157,16 @@ func TestTTLEviction(t *testing.T) {
|
|||
}
|
||||
evictedMu.Unlock()
|
||||
|
||||
done2() // effective
|
||||
done2() // ignored
|
||||
done2() // ignored
|
||||
done2(false) // effective
|
||||
done2(false) // ignored
|
||||
done2(false) // ignored
|
||||
evictedMu.Lock()
|
||||
if len(evicted) != 1 {
|
||||
t.Fatalf("only 1 content must be evicted")
|
||||
}
|
||||
evictedMu.Unlock()
|
||||
|
||||
done22()
|
||||
done22(false)
|
||||
evictedMu.Lock()
|
||||
if len(evicted) != 2 {
|
||||
t.Fatalf("2 contents must be evicted")
|
||||
|
@ -176,3 +176,29 @@ func TestTTLEviction(t *testing.T) {
|
|||
}
|
||||
evictedMu.Unlock()
|
||||
}
|
||||
|
||||
// TestTTLQuickDone tests the case where "done" with the explicit evict
|
||||
// is called before TTL
|
||||
func TestTTLQuickDone(t *testing.T) {
|
||||
var evicted []string
|
||||
c := NewTTLCache(time.Hour)
|
||||
c.OnEvicted = func(key string, value interface{}) {
|
||||
evicted = append(evicted, key)
|
||||
}
|
||||
key1, value1 := "key1", "abcd1"
|
||||
_, done1, _ := c.Add(key1, value1)
|
||||
_, done12, _ := c.Get(key1)
|
||||
|
||||
done1(false)
|
||||
if len(evicted) != 0 {
|
||||
t.Fatalf("no content must be evicted until all reference are discarded")
|
||||
}
|
||||
|
||||
done12(true)
|
||||
if len(evicted) != 1 {
|
||||
t.Fatalf("content must be evicted on an explicit evict before TTL")
|
||||
}
|
||||
if evicted[0] != key1 {
|
||||
t.Fatalf("1st content %q must be evicted but got %q", key1, evicted[0])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue