diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..21aab49b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +# Build artifacts +bin/ +*.deb +*.rpm + +# Test data and mocks +ci/e2e/resources/ +cmd/convertor/testingresources/mocks/ + +# Documentation +docs/ +*.md +!README.md + +# Git +.git/ +.gitignore + +# IDE files +.vscode/ +.idea/ +*.swp +*.swo + +# OS files +.DS_Store +Thumbs.db + +# Temporary files +*.tmp +*.temp +/tmp/ \ No newline at end of file diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 742eb2a0..73442a9c 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,5 +1,6 @@ name: Check on: + workflow_dispatch: push: branches: - main diff --git a/.github/workflows/ci-basic.yml b/.github/workflows/ci-basic.yml index 03b1f1da..b6d9d68a 100644 --- a/.github/workflows/ci-basic.yml +++ b/.github/workflows/ci-basic.yml @@ -1,5 +1,6 @@ name: CI | Basic on: + workflow_dispatch: workflow_call: inputs: image-tag: diff --git a/.github/workflows/ci-build-image.yml b/.github/workflows/ci-build-image.yml index 770a5b66..580404cb 100644 --- a/.github/workflows/ci-build-image.yml +++ b/.github/workflows/ci-build-image.yml @@ -1,5 +1,6 @@ name: CI | Build Image on: + workflow_dispatch: workflow_call: inputs: commit-hash: diff --git a/.github/workflows/ci-e2e.yml b/.github/workflows/ci-e2e.yml index 3202290d..8e6add54 100644 --- a/.github/workflows/ci-e2e.yml +++ b/.github/workflows/ci-e2e.yml @@ -1,5 +1,6 @@ name: CI | E2E on: + workflow_dispatch: workflow_call: inputs: commit-hash: diff --git a/.github/workflows/ci-unit-test.yml b/.github/workflows/ci-unit-test.yml index b15b33a1..15a9d435 100644 --- a/.github/workflows/ci-unit-test.yml +++ b/.github/workflows/ci-unit-test.yml @@ -1,5 +1,6 @@ name: Unit Test on: + workflow_dispatch: push: branches: - main diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c51bbdb..eef8a60c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,6 @@ name: CI on: + workflow_dispatch: push: branches: - main diff --git a/.gitignore b/.gitignore index cb4bc0e1..30501d20 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ bin/ .vscode vendor/ tmp_conv/ -tmp/ \ No newline at end of file +tmp/ +*.deb diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..1161d575 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,127 @@ +# Development Guide for Claude + +This document provides development guidance for working with the accelerated-container-image project. + +## Building + +### Production Build (Linux) +The project is designed to run on Linux. Use the Makefile for production builds: + +```bash +# Build all binaries +make binaries + +# Build specific binary +make bin/overlaybd-snapshotter + +# Clean build artifacts +make clean +``` + +### Development Build (macOS) +For development and testing on macOS, you can build with Linux target: + +```bash +# Build for Linux from macOS +GOOS=linux go build -o bin/overlaybd-snapshotter ./cmd/overlaybd-snapshotter/ + +# Build other components +GOOS=linux go build -o bin/ctr ./cmd/ctr/ +GOOS=linux go build -o bin/convertor ./cmd/convertor/ +``` + +**Note**: The binaries won't run on macOS due to Linux-specific dependencies (overlayFS, disk quotas, etc.), but this is useful for compilation testing and development. + +## Testing + +### Unit Tests +```bash +# Run tests (requires root on Linux) +make test + +# Run specific package tests +go test ./pkg/snapshot/... +go test ./internal/log/... +``` + +### Syntax Validation +```bash +# Check syntax without building +go list ./cmd/... ./pkg/... ./internal/... + +# Format code +go fmt ./... + +# Vet code +go vet ./... +``` + +## Code Formatting + +The project uses standard Go formatting: + +```bash +# Format all Go files +go fmt ./... + +# Check formatting +gofmt -l . + +# Fix imports +go mod tidy +``` + +## Linting + +```bash +# Run go vet +go vet ./... + +# Check for common issues +go mod verify +``` + +## Platform-Specific Notes + +### Linux-Only Components +- `pkg/snapshot/diskquota/` - Project quota management +- OverlayFS mounting functionality +- Block device operations + +### Cross-Platform Components +- gRPC service interfaces +- Logging and metrics +- Configuration parsing +- Basic snapshot logic + +## Development Workflow + +1. **Make changes** to Go files +2. **Test compilation**: `GOOS=linux go build ./cmd/overlaybd-snapshotter/` +3. **Format code**: `go fmt ./...` +4. **Validate**: `go vet ./...` +5. **Test on Linux** (if available): `make test` + +## Package Structure + +``` +cmd/ +├── overlaybd-snapshotter/ # Main snapshotter service +├── ctr/ # Container runtime tool +└── convertor/ # Image conversion tool + +pkg/ +├── snapshot/ # Core snapshotter implementation +├── metrics/ # Prometheus metrics +└── utils/ # Utility functions + +internal/ +└── log/ # Internal logging utilities +``` + +## Tips + +- **macOS Development**: Use `GOOS=linux` for all builds +- **Testing**: Full testing requires Linux environment +- **Dependencies**: Run `go mod tidy` after adding new imports +- **Build Errors**: Check build constraints (`//go:build linux`) if compilation fails diff --git a/Makefile b/Makefile index 8d86e633..a26a76f0 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,12 @@ SN_DESTDIR=/opt/overlaybd/snapshotter SN_CFGDIR=/etc/overlaybd-snapshotter +# versioning +RELEASE_VERSION?=1.3.0-runloop +RELEASE_NUM?=1 +OBD_VERSION?=1.0.15 +GO_VERSION?=1.23 + # command COMMANDS=overlaybd-snapshotter ctr convertor BINARIES=$(addprefix bin/,$(COMMANDS)) @@ -32,3 +38,43 @@ test: ## run tests that require root clean: @rm -rf ./bin + @rm -f *.deb + +deb-amd64: ## build .deb package for amd64 + @echo "Building .deb package for amd64..." + @mkdir -p /tmp/.buildx-cache + @DOCKER_BUILDKIT=1 docker buildx build \ + --platform linux/amd64 \ + --build-arg GO_VERSION=$(GO_VERSION) \ + --build-arg RELEASE_VERSION=$(RELEASE_VERSION) \ + --build-arg RELEASE_NUM=$(RELEASE_NUM) \ + --cache-from type=local,src=/tmp/.buildx-cache \ + --cache-to type=local,dest=/tmp/.buildx-cache \ + -f ci/build_image/Dockerfile.build_deb \ + --target deb-only \ + -t aci-builder-amd64 \ + --load . + @docker run --rm -v $(PWD):/output aci-builder-amd64 \ + sh -c "cp /app/overlaybd-snapshotter_*.deb /output/" + +deb-arm64: ## build .deb package for arm64 + @echo "Building .deb package for arm64..." + @mkdir -p /tmp/.buildx-cache + @DOCKER_BUILDKIT=1 docker buildx build \ + --platform linux/arm64 \ + --build-arg GO_VERSION=$(GO_VERSION) \ + --build-arg RELEASE_VERSION=$(RELEASE_VERSION) \ + --build-arg RELEASE_NUM=$(RELEASE_NUM) \ + --cache-from type=local,src=/tmp/.buildx-cache \ + --cache-to type=local,dest=/tmp/.buildx-cache \ + -f ci/build_image/Dockerfile.build_deb \ + --target deb-only \ + -t aci-builder-arm64 \ + --load . + @docker run --rm -v $(PWD):/output aci-builder-arm64 \ + sh -c "cp /app/overlaybd-snapshotter_*.deb /output/" + +deb: deb-amd64 deb-arm64 ## build .deb packages for both amd64 and arm64 + +help: ## show this help + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/ci/build_image/Dockerfile b/ci/build_image/Dockerfile index 4200a901..4b261314 100644 --- a/ci/build_image/Dockerfile +++ b/ci/build_image/Dockerfile @@ -17,12 +17,15 @@ ARG GO_VERSION ARG GO_IMAGE=golang:${GO_VERSION} FROM --platform=${BUILDPLATFORM} ${GO_IMAGE} AS builder WORKDIR /src -COPY . . +# Copy go.mod and go.sum first for better layer caching +COPY go.mod go.sum ./ ENV DEBIAN_FRONTEND=noninteractive RUN echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | tee /etc/apt/sources.list.d/goreleaser.list && \ apt update && \ apt install -y nfpm && \ - go mod tidy + go mod download +# Copy the rest of the source code +COPY . . ARG TARGETOS TARGETARCH ENV GOOS=${TARGETOS} @@ -37,29 +40,47 @@ RUN make && \ nfpm pkg --packager rpm --target /tmp/ # build image -FROM ubuntu:22.04 AS release +FROM ubuntu:24.04 AS release ARG OBD_VERSION ARG RELEASE_VERSION SHELL ["/bin/bash", "-c"] WORKDIR /app -COPY --from=builder /tmp/overlaybd-snapshotter_${RELEASE_VERSION}_amd64.deb . -COPY ./ci/build_image/start_services.sh . +# Install system dependencies first for better caching RUN apt-get update && apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release software-properties-common && \ - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Add Docker repository and install Docker components +RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \ echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null && \ apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io && \ - apt-get install -y libnl-3-200 libnl-genl-3-200 libcurl4-openssl-dev libaio-dev wget less kmod && \ - apt-get clean && rm -rf /var/lib/apt/lists/* && \ - wget https://github.com/containerd/overlaybd/releases/download/v${OBD_VERSION}/overlaybd-${OBD_VERSION}-20240717.b5b704b.ubuntu1.22.04.x86_64.deb && \ - dpkg -i overlaybd-${OBD_VERSION}-20240717.b5b704b.ubuntu1.22.04.x86_64.deb && \ - dpkg -i overlaybd-snapshotter_${RELEASE_VERSION}_amd64.deb && \ - sed -i 's/"autoRemoveDev": false,/"autoRemoveDev": true,/g' /etc/overlaybd-snapshotter/config.json && \ - cat /etc/overlaybd-snapshotter/config.json && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install runtime dependencies +RUN apt-get update && apt-get install -y libnl-3-200 libnl-genl-3-200 libcurl4-openssl-dev libaio-dev wget less kmod && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Download and install overlaybd (architecture-specific) +RUN case "$(dpkg --print-architecture)" in \ + amd64) OBD_ARCH=x86_64 ;; \ + arm64) OBD_ARCH=aarch64 ;; \ + *) echo "Unsupported architecture: $(dpkg --print-architecture)"; exit 1 ;; \ + esac && \ + wget https://github.com/containerd/overlaybd/releases/download/v${OBD_VERSION}/overlaybd-${OBD_VERSION}-20250610.859f8c8.ubuntu1.24.04.${OBD_ARCH}.deb && \ + dpkg -i overlaybd-${OBD_VERSION}-20250610.859f8c8.ubuntu1.24.04.${OBD_ARCH}.deb && \ + rm overlaybd-${OBD_VERSION}-20250610.859f8c8.ubuntu1.24.04.${OBD_ARCH}.deb + +# Copy and install the built snapshotter package +COPY --from=builder /tmp/overlaybd-snapshotter_*.deb . +RUN dpkg -i overlaybd-snapshotter_*.deb + +# Configure the snapshotter +RUN sed -i 's/"autoRemoveDev": false,/"autoRemoveDev": true,/g' /etc/overlaybd-snapshotter/config.json && \ mkdir -p /etc/containerd/ && \ - echo -e '[proxy_plugins.overlaybd]\n\ttype = "snapshot"\n\taddress = "/run/overlaybd-snapshotter/overlaybd.sock"' | tee -a /etc/containerd/config.toml && \ - cat /etc/containerd/config.toml && \ - chmod +x /app/start_services.sh && \ - cat /app/start_services.sh \ No newline at end of file + echo -e '[proxy_plugins.overlaybd]\n\ttype = "snapshot"\n\taddress = "/run/overlaybd-snapshotter/overlaybd.sock"' | tee -a /etc/containerd/config.toml + +# Copy startup script (only needed for running, not for deb extraction) +COPY ./ci/build_image/start_services.sh . +RUN chmod +x /app/start_services.sh \ No newline at end of file diff --git a/ci/build_image/Dockerfile.build_deb b/ci/build_image/Dockerfile.build_deb new file mode 100644 index 00000000..cec8a0e8 --- /dev/null +++ b/ci/build_image/Dockerfile.build_deb @@ -0,0 +1,45 @@ +# Copyright The Accelerated Container Image 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. + +# build overlaybd-snapshotter +ARG GO_VERSION +ARG GO_IMAGE=golang:${GO_VERSION} +FROM --platform=${BUILDPLATFORM} ${GO_IMAGE} AS builder +WORKDIR /src +# Copy go.mod and go.sum first for better layer caching +COPY go.mod go.sum ./ +ENV DEBIAN_FRONTEND=noninteractive +RUN echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | tee /etc/apt/sources.list.d/goreleaser.list && \ + apt update && \ + apt install -y nfpm && \ + go mod download +# Copy the rest of the source code +COPY . . + +ARG TARGETOS TARGETARCH +ENV GOOS=${TARGETOS} +ENV GOARCH=${TARGETARCH} +ARG RELEASE_VERSION +ENV SEMVER=${RELEASE_VERSION} +ARG RELEASE_NUM +ENV RELEASE=${RELEASE_NUM} +ENV COMMIT_ID="${RELEASE_VERSION}_${RELEASE_NUM}" +RUN make && \ + nfpm pkg --packager deb --target /tmp/ && \ + nfpm pkg --packager rpm --target /tmp/ + +# minimal image for deb extraction +FROM ubuntu:24.04 AS deb-only +WORKDIR /app +COPY --from=builder /tmp/overlaybd-snapshotter_*.deb . \ No newline at end of file diff --git a/cmd/convertor/builder/builder.go b/cmd/convertor/builder/builder.go index 5ed922f7..9e25cc1f 100644 --- a/cmd/convertor/builder/builder.go +++ b/cmd/convertor/builder/builder.go @@ -34,6 +34,7 @@ import ( "time" "github.com/containerd/accelerated-container-image/cmd/convertor/database" + "github.com/containerd/accelerated-container-image/pkg/utils" "github.com/containerd/containerd/v2/core/images" "github.com/containerd/containerd/v2/core/remotes" "github.com/containerd/containerd/v2/core/remotes/docker" @@ -73,6 +74,10 @@ type BuilderOptions struct { // Push manifests with subject Referrer bool + + // CustomResolver allows using a custom resolver instead of the default docker resolver + // Used for tar import/export functionality + CustomResolver remotes.Resolver } type graphBuilder struct { @@ -129,6 +134,16 @@ func (b *graphBuilder) Build(ctx context.Context) error { } func (b *graphBuilder) process(ctx context.Context, src v1.Descriptor, tag bool) (v1.Descriptor, error) { + // Skip provenance and attestation manifests (check by platform and content) + if src.Platform != nil && src.Platform.OS == "unknown" && src.Platform.Architecture == "unknown" { + // This might be a provenance manifest, check if it should be skipped + if utils.IsProvenanceDescriptor(src) { + log.G(ctx).Infof("skipping provenance manifest: %s (platform: %s/%s)", src.Digest, src.Platform.OS, src.Platform.Architecture) + // Return a special "skipped" descriptor instead of an error + return v1.Descriptor{}, nil + } + } + switch src.MediaType { case v1.MediaTypeImageManifest, images.MediaTypeDockerSchema2Manifest: return b.buildOne(ctx, src, tag) @@ -147,17 +162,24 @@ func (b *graphBuilder) process(ctx context.Context, src v1.Descriptor, tag bool) return v1.Descriptor{}, fmt.Errorf("failed to unmarshal index: %w", err) } var wg sync.WaitGroup - for _i, _m := range index.Manifests { - i := _i - m := _m + var mu sync.Mutex + var filteredManifests []v1.Descriptor + + for _, m := range index.Manifests { + manifest := m wg.Add(1) b.group.Go(func() error { defer wg.Done() - target, err := b.process(ctx, m, false) + target, err := b.process(ctx, manifest, false) if err != nil { - return fmt.Errorf("failed to build %q: %w", m.Digest, err) + return fmt.Errorf("failed to build %q: %w", manifest.Digest, err) + } + // Only add non-empty descriptors (skip provenance manifests) + if target.Digest != "" { + mu.Lock() + filteredManifests = append(filteredManifests, target) + mu.Unlock() } - index.Manifests[i] = target return nil }) } @@ -165,6 +187,9 @@ func (b *graphBuilder) process(ctx context.Context, src v1.Descriptor, tag bool) if ctx.Err() != nil { return v1.Descriptor{}, ctx.Err() } + + // Update index with only the non-provenance manifests + index.Manifests = filteredManifests // upload index if b.Referrer { @@ -299,46 +324,55 @@ func (b *graphBuilder) buildOne(ctx context.Context, src v1.Descriptor, tag bool } func Build(ctx context.Context, opt BuilderOptions) error { - tlsConfig, err := loadTLSConfig(opt.CertOption) - if err != nil { - return fmt.Errorf("failed to load certifications: %w", err) - } - transport := &http.Transport{ - DialContext: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - FallbackDelay: 300 * time.Millisecond, - }).DialContext, - MaxConnsPerHost: 32, // max http concurrency - MaxIdleConns: 32, - IdleConnTimeout: 30 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - TLSClientConfig: tlsConfig, - ExpectContinueTimeout: 5 * time.Second, - } - client := &http.Client{Transport: transport} - resolver := docker.NewResolver(docker.ResolverOptions{ - Hosts: docker.ConfigureDefaultRegistries( - docker.WithAuthorizer(docker.NewDockerAuthorizer( - docker.WithAuthClient(client), - docker.WithAuthHeader(make(http.Header)), - docker.WithAuthCreds(func(s string) (string, string, error) { - if i := strings.IndexByte(opt.Auth, ':'); i > 0 { - return opt.Auth[0:i], opt.Auth[i+1:], nil + var resolver remotes.Resolver + + // Use custom resolver if provided, otherwise create default docker resolver + if opt.CustomResolver != nil { + log.G(ctx).Info("using custom resolver (tar import mode)") + resolver = opt.CustomResolver + } else { + log.G(ctx).Info("using docker registry resolver") + tlsConfig, err := loadTLSConfig(opt.CertOption) + if err != nil { + return fmt.Errorf("failed to load certifications: %w", err) + } + transport := &http.Transport{ + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + FallbackDelay: 300 * time.Millisecond, + }).DialContext, + MaxConnsPerHost: 32, // max http concurrency + MaxIdleConns: 32, + IdleConnTimeout: 30 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + TLSClientConfig: tlsConfig, + ExpectContinueTimeout: 5 * time.Second, + } + client := &http.Client{Transport: transport} + resolver = docker.NewResolver(docker.ResolverOptions{ + Hosts: docker.ConfigureDefaultRegistries( + docker.WithAuthorizer(docker.NewDockerAuthorizer( + docker.WithAuthClient(client), + docker.WithAuthHeader(make(http.Header)), + docker.WithAuthCreds(func(s string) (string, string, error) { + if i := strings.IndexByte(opt.Auth, ':'); i > 0 { + return opt.Auth[0:i], opt.Auth[i+1:], nil + } + return "", "", nil + }), + )), + docker.WithClient(client), + docker.WithPlainHTTP(func(s string) (bool, error) { + if opt.PlainHTTP { + return docker.MatchAllHosts(s) + } else { + return false, nil } - return "", "", nil }), - )), - docker.WithClient(client), - docker.WithPlainHTTP(func(s string) (bool, error) { - if opt.PlainHTTP { - return docker.MatchAllHosts(s) - } else { - return false, nil - } - }), - ), - }) + ), + }) + } return (&graphBuilder{ Resolver: resolver, diff --git a/cmd/convertor/builder/content_store_resolver.go b/cmd/convertor/builder/content_store_resolver.go new file mode 100644 index 00000000..cd1048ad --- /dev/null +++ b/cmd/convertor/builder/content_store_resolver.go @@ -0,0 +1,502 @@ +/* + Copyright The Accelerated Container Image 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 builder + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "time" + + "github.com/containerd/accelerated-container-image/pkg/utils" + "github.com/containerd/containerd/v2/core/content" + "github.com/containerd/containerd/v2/core/images" + "github.com/containerd/containerd/v2/core/images/archive" + "github.com/containerd/containerd/v2/core/remotes" + "github.com/containerd/containerd/v2/core/remotes/docker" + "github.com/containerd/containerd/v2/plugins/content/local" + "github.com/containerd/errdefs" + "github.com/containerd/log" + "github.com/opencontainers/go-digest" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +// ContentStoreResolver implements remotes.Resolver using a content store +type ContentStoreResolver struct { + store content.Store + imageStore images.Store + tracker docker.StatusTracker +} + +// Store returns the underlying content store +func (r *ContentStoreResolver) Store() content.Store { + return r.store +} + +// ImageStore returns the underlying image store +func (r *ContentStoreResolver) ImageStore() images.Store { + return r.imageStore +} + +// NewContentStoreResolver creates a new resolver backed by a content store +func NewContentStoreResolver(store content.Store, imageStore images.Store) *ContentStoreResolver { + return &ContentStoreResolver{ + store: store, + imageStore: imageStore, + tracker: docker.NewInMemoryTracker(), + } +} + +// NewContentStoreResolverFromTar creates a resolver by importing a tar file +func NewContentStoreResolverFromTar(ctx context.Context, tarPath string) (*ContentStoreResolver, error) { + // Create temporary directory for content store + tempDir, err := os.MkdirTemp("", "convertor-import-*") + if err != nil { + return nil, errors.Wrapf(err, "failed to create temp directory") + } + + // Create local content store + store, err := local.NewStore(tempDir) + if err != nil { + return nil, errors.Wrapf(err, "failed to create content store") + } + + // Create in-memory image store + imageStore := &memoryImageStore{ + images: make(map[string]images.Image), + } + + // Open tar file + tarFile, err := os.Open(tarPath) + if err != nil { + return nil, errors.Wrapf(err, "failed to open tar file %s", tarPath) + } + defer tarFile.Close() + + // Import tar into content store + log.G(ctx).Infof("importing tar file: %s", tarPath) + indexDesc, err := archive.ImportIndex(ctx, store, tarFile, + archive.WithImportCompression(), + ) + if err != nil { + return nil, errors.Wrapf(err, "failed to import tar file") + } + + // Read the actual index content from the content store + indexContent, err := content.ReadBlob(ctx, store, indexDesc) + if err != nil { + return nil, errors.Wrapf(err, "failed to read imported index") + } + + var index v1.Index + if err := json.Unmarshal(indexContent, &index); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal index") + } + + // Store image references (skip provenance layers) + log.G(ctx).Infof("processing %d manifests from index", len(index.Manifests)) + for i, manifest := range index.Manifests { + log.G(ctx).Infof("=== Processing manifest %d/%d ===", i+1, len(index.Manifests)) + log.G(ctx).Infof(" MediaType: %s", manifest.MediaType) + log.G(ctx).Infof(" Digest: %s", manifest.Digest) + log.G(ctx).Infof(" Size: %d bytes", manifest.Size) + + if manifest.Platform != nil { + log.G(ctx).Infof(" Platform: %s/%s", manifest.Platform.OS, manifest.Platform.Architecture) + if manifest.Platform.Variant != "" { + log.G(ctx).Infof(" Platform Variant: %s", manifest.Platform.Variant) + } + } + + if manifest.ArtifactType != "" { + log.G(ctx).Infof(" ArtifactType: %s", manifest.ArtifactType) + } + + if manifest.Annotations != nil && len(manifest.Annotations) > 0 { + log.G(ctx).Infof(" Annotations:") + for key, value := range manifest.Annotations { + log.G(ctx).Infof(" %s: %s", key, value) + } + } + + // Skip provenance and attestation manifests + if isProvenanceManifestWithContent(ctx, store, manifest) { + log.G(ctx).Infof(" ❌ SKIPPING: Detected as provenance/attestation manifest") + continue + } + + // Check if this is an image index (multi-arch) + if manifest.MediaType == v1.MediaTypeImageIndex || manifest.MediaType == "application/vnd.docker.distribution.manifest.list.v2+json" { + log.G(ctx).Infof(" 📁 FOUND IMAGE INDEX: Importing as-is for multi-arch conversion") + + // Import the index itself - let the builder handle platform traversal + ref := fmt.Sprintf("imported:%s", manifest.Digest.Encoded()[:12]) + if manifest.Annotations != nil { + if name, ok := manifest.Annotations["org.opencontainers.image.ref.name"]; ok { + ref = name + log.G(ctx).Infof(" Using annotation-based ref: %s", ref) + } + } + + image := images.Image{ + Name: ref, + Target: manifest, + } + imageStore.Create(ctx, image) + log.G(ctx).Infof(" ✅ IMPORTED INDEX: %s -> %s (will be processed as multi-arch)", ref, manifest.Digest) + continue + } + + log.G(ctx).Infof(" ✅ PROCESSING: Regular container manifest") + + // Generate a reference name for the imported image + ref := fmt.Sprintf("imported:%s", manifest.Digest.Encoded()[:12]) + if manifest.Annotations != nil { + if name, ok := manifest.Annotations["org.opencontainers.image.ref.name"]; ok { + ref = name + log.G(ctx).Infof(" Using annotation-based ref: %s", ref) + } + } + + image := images.Image{ + Name: ref, + Target: manifest, + } + imageStore.Create(ctx, image) + log.G(ctx).Infof(" ✅ IMPORTED: %s -> %s", ref, manifest.Digest) + } + + return NewContentStoreResolver(store, imageStore), nil +} + +// Resolve resolves a reference to a descriptor +func (r *ContentStoreResolver) Resolve(ctx context.Context, ref string) (string, v1.Descriptor, error) { + log.G(ctx).Debugf("resolving reference: %s", ref) + + // Parse reference + name, tag := parseRef(ref) + + // Look up in image store + image, err := r.imageStore.Get(ctx, name) + if err != nil { + // Try with tag appended + if tag != "" { + image, err = r.imageStore.Get(ctx, ref) + } + if err != nil { + return "", v1.Descriptor{}, errors.Wrapf(errdefs.ErrNotFound, "image not found: %s", ref) + } + } + + return ref, image.Target, nil +} + +// Fetcher returns a fetcher for the content store +func (r *ContentStoreResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) { + return &ContentStoreFetcher{ + store: r.store, + }, nil +} + +// Pusher returns a pusher for the content store +func (r *ContentStoreResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) { + return &ContentStorePusher{ + store: r.store, + imageStore: r.imageStore, + ref: ref, + tracker: r.tracker, + }, nil +} + +// ContentStoreFetcher implements remotes.Fetcher using a content store +type ContentStoreFetcher struct { + store content.Store +} + +// Fetch fetches content from the content store +func (f *ContentStoreFetcher) Fetch(ctx context.Context, desc v1.Descriptor) (io.ReadCloser, error) { + log.G(ctx).Debugf("fetching blob: %s", desc.Digest) + + reader, err := f.store.ReaderAt(ctx, desc) + if err != nil { + return nil, errors.Wrapf(err, "failed to get reader for %s", desc.Digest) + } + + return &readerAtCloser{ReaderAt: reader, offset: 0}, nil +} + +// ContentStorePusher implements remotes.Pusher using a content store +type ContentStorePusher struct { + store content.Store + imageStore images.Store + ref string + tracker docker.StatusTracker +} + +// Push pushes content to the content store +func (p *ContentStorePusher) Push(ctx context.Context, desc v1.Descriptor) (content.Writer, error) { + log.G(ctx).Debugf("pushing blob: %s", desc.Digest) + + // Check if content already exists + _, err := p.store.Info(ctx, desc.Digest) + if err == nil { + log.G(ctx).Debugf("content already exists: %s", desc.Digest) + return &nopWriter{desc: desc}, nil + } + + // Create writer + writer, err := p.store.Writer(ctx, content.WithRef(remotes.MakeRefKey(ctx, desc)), content.WithDescriptor(desc)) + if err != nil { + return nil, errors.Wrapf(err, "failed to create writer for %s", desc.Digest) + } + + return &trackingWriter{ + Writer: writer, + desc: desc, + tracker: p.tracker, + ref: remotes.MakeRefKey(ctx, desc), + }, nil +} + +// Writer returns a content writer (not used by overlaybd conversion) +func (p *ContentStorePusher) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { + return nil, errors.New("Writer not implemented") +} + +// Helper types and functions + +// readerAtCloser wraps content.ReaderAt to implement io.ReadCloser +type readerAtCloser struct { + content.ReaderAt + offset int64 +} + +func (r *readerAtCloser) Read(p []byte) (int, error) { + n, err := r.ReadAt(p, r.offset) + r.offset += int64(n) + return n, err +} + +func (r *readerAtCloser) Close() error { + return r.ReaderAt.Close() +} + +// nopWriter implements content.Writer for already existing content +type nopWriter struct { + desc v1.Descriptor +} + +func (w *nopWriter) Write(p []byte) (int, error) { + return len(p), nil +} + +func (w *nopWriter) Close() error { + return nil +} + +func (w *nopWriter) Digest() digest.Digest { + return w.desc.Digest +} + +func (w *nopWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { + return nil +} + +func (w *nopWriter) Status() (content.Status, error) { + return content.Status{ + Ref: w.desc.Digest.String(), + Offset: w.desc.Size, + Total: w.desc.Size, + }, nil +} + +func (w *nopWriter) Truncate(size int64) error { + return nil +} + +// trackingWriter wraps content.Writer with progress tracking +type trackingWriter struct { + content.Writer + desc v1.Descriptor + tracker docker.StatusTracker + ref string +} + +func (w *trackingWriter) Write(p []byte) (int, error) { + n, err := w.Writer.Write(p) + if err == nil && w.tracker != nil { + status, _ := w.tracker.GetStatus(w.ref) + status.Offset += int64(n) + status.UpdatedAt = time.Now() + w.tracker.SetStatus(w.ref, status) + } + return n, err +} + +func (w *trackingWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { + err := w.Writer.Commit(ctx, size, expected, opts...) + if err == nil && w.tracker != nil { + status, _ := w.tracker.GetStatus(w.ref) + status.Committed = true + status.UpdatedAt = time.Now() + w.tracker.SetStatus(w.ref, status) + } + return err +} + +// memoryImageStore implements images.Store in memory +type memoryImageStore struct { + images map[string]images.Image +} + +func (s *memoryImageStore) Get(ctx context.Context, name string) (images.Image, error) { + if img, ok := s.images[name]; ok { + return img, nil + } + return images.Image{}, errdefs.ErrNotFound +} + +func (s *memoryImageStore) List(ctx context.Context, filters ...string) ([]images.Image, error) { + var result []images.Image + for _, img := range s.images { + result = append(result, img) + } + return result, nil +} + +func (s *memoryImageStore) Create(ctx context.Context, image images.Image) (images.Image, error) { + s.images[image.Name] = image + return image, nil +} + +func (s *memoryImageStore) Update(ctx context.Context, image images.Image, fieldpaths ...string) (images.Image, error) { + s.images[image.Name] = image + return image, nil +} + +func (s *memoryImageStore) Delete(ctx context.Context, name string, opts ...images.DeleteOpt) error { + delete(s.images, name) + return nil +} + +// parseRef parses a reference into name and tag +func parseRef(ref string) (name, tag string) { + if idx := strings.LastIndex(ref, ":"); idx != -1 { + return ref[:idx], ref[idx+1:] + } + return ref, "" +} + +// ExportContentStoreToTar exports a content store to a tar file +func ExportContentStoreToTar(ctx context.Context, store content.Store, imageStore images.Store, tarPath string) error { + // Create tar file + if err := os.MkdirAll(filepath.Dir(tarPath), 0755); err != nil { + return errors.Wrapf(err, "failed to create directory for tar file") + } + + tarFile, err := os.Create(tarPath) + if err != nil { + return errors.Wrapf(err, "failed to create tar file %s", tarPath) + } + defer tarFile.Close() + + // List all images + imageList, err := imageStore.List(ctx) + if err != nil { + return errors.Wrapf(err, "failed to list images") + } + + if len(imageList) == 0 { + return errors.New("no images to export") + } + + log.G(ctx).Infof("exporting %d images to tar file: %s", len(imageList), tarPath) + + // Export to tar with each image as a separate option + var exportOpts []archive.ExportOpt + for _, img := range imageList { + exportOpts = append(exportOpts, archive.WithImage(imageStore, img.Name)) + } + exportOpts = append(exportOpts, archive.WithAllPlatforms()) + + return archive.Export(ctx, store, tarFile, exportOpts...) +} + +// isProvenanceManifest is a wrapper around the shared utils function with debug logging +func isProvenanceManifest(desc v1.Descriptor) bool { + if utils.IsProvenanceDescriptor(desc) { + log.L.Debugf("🔍 FILTERING: Provenance descriptor detected: %s", desc.Digest.Encoded()[:12]) + return true + } + log.L.Debugf("🔍 NOT FILTERING: No provenance markers found for digest: %s", desc.Digest.Encoded()[:12]) + return false +} + +// isProvenanceManifestWithContent checks if a manifest descriptor represents provenance/attestation metadata +// by examining both the descriptor metadata and the actual manifest content +func isProvenanceManifestWithContent(ctx context.Context, store content.Store, desc v1.Descriptor) bool { + log.G(ctx).Debugf(" 🔍 Checking if manifest is provenance: %s", desc.Digest) + + // First check descriptor metadata + if isProvenanceManifest(desc) { + log.G(ctx).Debugf(" 🔍 Detected provenance via descriptor metadata") + return true + } + + // For generic manifest media types, we need to check the content + if desc.MediaType == "application/vnd.docker.distribution.manifest.v2+json" || + desc.MediaType == "application/vnd.oci.image.manifest.v1+json" { + + log.G(ctx).Debugf(" 🔍 Generic manifest media type, reading content to inspect...") + + // Read the manifest content + manifestContent, err := content.ReadBlob(ctx, store, desc) + if err != nil { + log.G(ctx).Debugf(" 🔍 Failed to read manifest content: %v", err) + // If we can't read it, assume it's not provenance + return false + } + + // Show first 200 chars of content for debugging + contentStr := string(manifestContent) + preview := contentStr + if len(preview) > 200 { + preview = preview[:200] + "..." + } + log.G(ctx).Debugf(" 🔍 Content preview: %s", preview) + + // Check if content contains in-toto attestation markers + if strings.Contains(contentStr, `"_type":"https://in-toto.io/`) || + strings.Contains(contentStr, `"predicateType":"https://spdx.dev/`) || + strings.Contains(contentStr, `"predicateType":"https://slsa.dev/`) || + strings.Contains(contentStr, `"predicateType":"https://in-toto.io/`) { + log.G(ctx).Debugf(" 🔍 FOUND provenance markers in content!") + return true + } + + log.G(ctx).Debugf(" 🔍 No provenance markers found in content") + } else { + log.G(ctx).Debugf(" 🔍 Non-generic media type, skipping content inspection") + } + + return false +} \ No newline at end of file diff --git a/cmd/convertor/builder/file_pusher.go b/cmd/convertor/builder/file_pusher.go new file mode 100644 index 00000000..e349732d --- /dev/null +++ b/cmd/convertor/builder/file_pusher.go @@ -0,0 +1,239 @@ +/* + Copyright The Accelerated Container Image 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 builder + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/containerd/containerd/v2/core/content" + "github.com/containerd/containerd/v2/core/images" + "github.com/containerd/containerd/v2/core/remotes" + "github.com/containerd/containerd/v2/plugins/content/local" + "github.com/containerd/errdefs" + "github.com/containerd/log" + "github.com/opencontainers/go-digest" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +// FileBasedResolver implements remotes.Resolver that captures pushed content locally +// for later export to tar files +type FileBasedResolver struct { + store content.Store + imageStore images.Store + outputStore content.Store // Where converted layers are stored + outputImageStore images.Store + tempDir string // Path to temporary directory for cleanup +} + +// NewFileBasedResolver creates a resolver that captures converted content locally +func NewFileBasedResolver(importStore content.Store, importImageStore images.Store) (*FileBasedResolver, error) { + // Create temporary directory for output content store + tempDir, err := os.MkdirTemp("", "convertor-output-*") + if err != nil { + return nil, errors.Wrapf(err, "failed to create temp directory for output store") + } + + // Create local content store for converted layers + outputStore, err := local.NewStore(tempDir) + if err != nil { + return nil, errors.Wrapf(err, "failed to create output content store") + } + + // Create in-memory image store for converted images + outputImageStore := &memoryImageStore{ + images: make(map[string]images.Image), + } + + return &FileBasedResolver{ + store: importStore, + imageStore: importImageStore, + outputStore: outputStore, + outputImageStore: outputImageStore, + tempDir: tempDir, + }, nil +} + +// Store returns the import content store (for reading original layers) +func (r *FileBasedResolver) Store() content.Store { + return r.store +} + +// ImageStore returns the import image store +func (r *FileBasedResolver) ImageStore() images.Store { + return r.imageStore +} + +// OutputStore returns the output content store (containing converted layers) +func (r *FileBasedResolver) OutputStore() content.Store { + return r.outputStore +} + +// OutputImageStore returns the output image store (containing converted manifests) +func (r *FileBasedResolver) OutputImageStore() images.Store { + return r.outputImageStore +} + +// Resolve resolves a reference from the import store +func (r *FileBasedResolver) Resolve(ctx context.Context, ref string) (string, v1.Descriptor, error) { + log.G(ctx).Debugf("file-based resolver: resolving reference: %s", ref) + + // Look up in import image store + image, err := r.imageStore.Get(ctx, ref) + if err != nil { + return "", v1.Descriptor{}, errors.Wrapf(errdefs.ErrNotFound, "image not found: %s", ref) + } + + return ref, image.Target, nil +} + +// Fetcher returns a fetcher for the import content store +func (r *FileBasedResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) { + return &ContentStoreFetcher{ + store: r.store, + }, nil +} + +// Pusher returns a file-based pusher that captures converted content locally +func (r *FileBasedResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) { + log.G(ctx).Debugf("file-based resolver: creating pusher for ref: %s", ref) + + return &FilePusher{ + ref: ref, + store: r.outputStore, + imageStore: r.outputImageStore, + }, nil +} + +// FilePusher implements remotes.Pusher by writing content to a local content store +type FilePusher struct { + ref string + store content.Store + imageStore images.Store +} + +// Push writes content to the local output content store +func (p *FilePusher) Push(ctx context.Context, desc v1.Descriptor) (content.Writer, error) { + log.G(ctx).Debugf("file pusher: pushing blob: %s (size: %d)", desc.Digest, desc.Size) + + // Check if content already exists + _, err := p.store.Info(ctx, desc.Digest) + if err == nil { + log.G(ctx).Debugf("file pusher: content already exists: %s", desc.Digest) + return &nopWriter{desc: desc}, nil + } + + // Create writer for the content + writer, err := p.store.Writer(ctx, content.WithRef(remotes.MakeRefKey(ctx, desc)), content.WithDescriptor(desc)) + if err != nil { + return nil, errors.Wrapf(err, "failed to create writer for %s", desc.Digest) + } + + return &fileWriter{ + Writer: writer, + desc: desc, + pusher: p, + }, nil +} + +// Writer returns a content writer (not used by overlaybd conversion) +func (p *FilePusher) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { + return nil, errors.New("Writer not implemented") +} + +// fileWriter wraps content.Writer and handles manifest storage +type fileWriter struct { + content.Writer + desc v1.Descriptor + pusher *FilePusher +} + +func (w *fileWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { + log.G(ctx).Debugf("file pusher: committing blob: %s", w.desc.Digest) + + err := w.Writer.Commit(ctx, size, expected, opts...) + if err != nil { + return err + } + + // If this is a manifest, store it in the image store as well + if isManifestMediaType(w.desc.MediaType) { + log.G(ctx).Debugf("file pusher: storing manifest in image store: %s", w.desc.Digest) + + // Generate image name based on the pusher's reference + imageName := fmt.Sprintf("converted:%s", w.desc.Digest.Encoded()[:12]) + if w.pusher.ref != "" { + // Extract name from reference (remove @digest part if present) + if idx := len(w.pusher.ref); idx > 0 { + imageName = w.pusher.ref + if atIdx := len(imageName); atIdx > 0 && imageName[atIdx-1:] == "@" { + imageName = imageName[:atIdx-1] + } + } + } + + image := images.Image{ + Name: imageName, + Target: w.desc, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + _, err = w.pusher.imageStore.Create(ctx, image) + if err != nil && !errdefs.IsAlreadyExists(err) { + log.G(ctx).Warnf("failed to store image in image store: %v", err) + } else { + log.G(ctx).Debugf("file pusher: stored image: %s -> %s", imageName, w.desc.Digest) + } + } + + return nil +} + +// isManifestMediaType checks if a media type represents a manifest +func isManifestMediaType(mediaType string) bool { + manifestTypes := []string{ + v1.MediaTypeImageManifest, + "application/vnd.docker.distribution.manifest.v2+json", + v1.MediaTypeImageIndex, + "application/vnd.docker.distribution.manifest.list.v2+json", + } + + for _, mt := range manifestTypes { + if mediaType == mt { + return true + } + } + return false +} + +// CleanupTempDir removes the temporary directory used by the output store +func (r *FileBasedResolver) CleanupTempDir() error { + if r.tempDir != "" { + log.L.Debugf("cleaning up temporary output directory: %s", r.tempDir) + return os.RemoveAll(r.tempDir) + } + return nil +} + +// GetTempDir returns the temporary directory path for debugging +func (r *FileBasedResolver) GetTempDir() string { + return r.tempDir +} \ No newline at end of file diff --git a/cmd/convertor/builder/overlaybd_builder.go b/cmd/convertor/builder/overlaybd_builder.go index b1fb135a..6fecfeff 100644 --- a/cmd/convertor/builder/overlaybd_builder.go +++ b/cmd/convertor/builder/overlaybd_builder.go @@ -175,6 +175,7 @@ func (e *overlaybdBuilderEngine) UploadLayer(ctx context.Context, idx int) error label.OverlayBDVersion: version.OverlayBDVersionNumber, label.OverlayBDBlobDigest: desc.Digest.String(), label.OverlayBDBlobSize: fmt.Sprintf("%d", desc.Size), + label.OverlayBDBlobFsType: e.fstype, } if !e.noUpload { if err := uploadBlob(ctx, e.pusher, path.Join(layerDir, commitFile), desc); err != nil { @@ -463,6 +464,7 @@ func (e *overlaybdBuilderEngine) uploadBaseLayer(ctx context.Context) (specs.Des label.OverlayBDVersion: version.OverlayBDVersionNumber, label.OverlayBDBlobDigest: digester.Digest().String(), label.OverlayBDBlobSize: fmt.Sprintf("%d", countWriter.c), + label.OverlayBDBlobFsType: e.fstype, }, } if !e.noUpload { diff --git a/cmd/convertor/main.go b/cmd/convertor/main.go index 8a071b49..367947e8 100644 --- a/cmd/convertor/main.go +++ b/cmd/convertor/main.go @@ -21,9 +21,11 @@ import ( "database/sql" "os" "os/signal" + "strings" "github.com/containerd/accelerated-container-image/cmd/convertor/builder" "github.com/containerd/accelerated-container-image/cmd/convertor/database" + "github.com/containerd/containerd/v2/core/remotes" _ "github.com/go-sql-driver/mysql" "github.com/sirupsen/logrus" @@ -53,6 +55,11 @@ var ( disableSparse bool referrer bool + // tar import/export + importTar string + exportTar string + tarExportRepo string + // certification certDirs []string rootCAs []string @@ -75,8 +82,16 @@ Version: ` + commitID, logrus.SetLevel(logrus.DebugLevel) } tb := "" - if digestInput == "" && tagInput == "" { - logrus.Error("one of input-tag [-i] or input-digest [-g] is required") + if importTar == "" && digestInput == "" && tagInput == "" { + logrus.Error("one of input-tag [-i], input-digest [-g], or import-tar is required") + os.Exit(1) + } + if importTar != "" && (digestInput != "" || tagInput != "") { + logrus.Error("import-tar cannot be used with input-tag or input-digest") + os.Exit(1) + } + if importTar == "" && repo == "" { + logrus.Error("repository is required when not using import-tar") os.Exit(1) } if overlaybd == "" && fastoci == "" && turboOCI == "" { @@ -98,31 +113,145 @@ Version: ` + commitID, } ctx := context.Background() - ref := repo + ":" + tagInput - if tagInput == "" { - ref = repo + "@" + digestInput - } - opt := builder.BuilderOptions{ - Ref: ref, - Auth: user, - PlainHTTP: plain, - WorkDir: dir, - OCI: oci, - FsType: fsType, - Mkfs: mkfs, - Vsize: vsize, - CertOption: builder.CertOption{ - CertDirs: certDirs, - RootCAs: rootCAs, - ClientCerts: clientCerts, - Insecure: insecure, - }, - Reserve: reserve, - NoUpload: noUpload, - DumpManifest: dumpManifest, - ConcurrencyLimit: concurrencyLimit, - DisableSparse: disableSparse, - Referrer: referrer, + + // Handle tar import/export mode + var opt builder.BuilderOptions + var importResolver *builder.ContentStoreResolver + var exportResolver *builder.FileBasedResolver + + if importTar != "" { + // Import mode - create content store resolver from tar + logrus.Infof("importing from tar file: %s", importTar) + var err error + importResolver, err = builder.NewContentStoreResolverFromTar(ctx, importTar) + if err != nil { + logrus.Errorf("failed to import tar file: %v", err) + os.Exit(1) + } + + // Find the multi-arch index to build all architectures + images, err := importResolver.ImageStore().List(ctx) + if err != nil || len(images) == 0 { + logrus.Error("no images found in tar file") + os.Exit(1) + } + + // Look for the main index (should have the original reference name) + var ref string + var isMultiArch bool + for _, img := range images { + // The main index usually has the original tag name (e.g., "latest") + // Platform-specific manifests have names like "latest-linux-amd64" + if !strings.Contains(img.Name, "-linux-") && !strings.Contains(img.Name, "imported:") { + ref = img.Name + isMultiArch = (img.Target.MediaType == "application/vnd.oci.image.index.v1+json") + break + } + } + + // Fallback: if no main index found, use first image + if ref == "" { + ref = images[0].Name + isMultiArch = (images[0].Target.MediaType == "application/vnd.oci.image.index.v1+json") + logrus.Warnf("no main index found, using first image: %s", ref) + } else { + logrus.Infof("found main image reference: %s", ref) + } + + // Log what we're building + if isMultiArch { + logrus.Infof("building multi-arch image with %d total imported images", len(images)) + } else { + logrus.Infof("building single-arch image: %s", ref) + } + + // Choose resolver based on export mode + if exportTar != "" { + // For tar export, use FileBasedResolver to capture converted layers locally + logrus.Infof("tar export mode: using file-based resolver to capture converted layers") + var err error + exportResolver, err = builder.NewFileBasedResolver(importResolver.Store(), importResolver.ImageStore()) + if err != nil { + logrus.Errorf("failed to create file-based resolver: %v", err) + os.Exit(1) + } + repo = tarExportRepo + + // Setup cleanup for export resolver temporary directory + defer func() { + if !reserve && exportResolver != nil { + if err := exportResolver.CleanupTempDir(); err != nil { + logrus.Warnf("failed to cleanup export temporary directory: %v", err) + } + } + }() + } else { + // For registry push, use import resolver directly + if repo == "" { + logrus.Error("repository is required when not using export-tar") + os.Exit(1) + } + } + + // Type assert to remotes.Resolver interface + var resolver remotes.Resolver + if exportResolver != nil { + resolver = exportResolver + } else { + resolver = importResolver + } + + opt = builder.BuilderOptions{ + Ref: ref, + Auth: user, + PlainHTTP: plain, + WorkDir: dir, + OCI: oci, + FsType: fsType, + Mkfs: mkfs, + Vsize: vsize, + CustomResolver: resolver, + CertOption: builder.CertOption{ + CertDirs: certDirs, + RootCAs: rootCAs, + ClientCerts: clientCerts, + Insecure: insecure, + }, + Reserve: reserve, + NoUpload: noUpload, + DumpManifest: dumpManifest, + ConcurrencyLimit: concurrencyLimit, + DisableSparse: disableSparse, + Referrer: referrer, + } + } else { + // Normal registry mode + ref := repo + ":" + tagInput + if tagInput == "" { + ref = repo + "@" + digestInput + } + opt = builder.BuilderOptions{ + Ref: ref, + Auth: user, + PlainHTTP: plain, + WorkDir: dir, + OCI: oci, + FsType: fsType, + Mkfs: mkfs, + Vsize: vsize, + CertOption: builder.CertOption{ + CertDirs: certDirs, + RootCAs: rootCAs, + ClientCerts: clientCerts, + Insecure: insecure, + }, + Reserve: reserve, + NoUpload: noUpload, + DumpManifest: dumpManifest, + ConcurrencyLimit: concurrencyLimit, + DisableSparse: disableSparse, + Referrer: referrer, + } } if overlaybd != "" { logrus.Info("building [Overlaybd - Native] image...") @@ -151,6 +280,16 @@ Version: ` + commitID, os.Exit(1) } logrus.Info("overlaybd build finished") + + // Handle tar export if requested + if exportTar != "" && exportResolver != nil { + logrus.Infof("exporting converted overlaybd layers to tar file: %s", exportTar) + if err := builder.ExportContentStoreToTar(ctx, exportResolver.OutputStore(), exportResolver.OutputImageStore(), exportTar); err != nil { + logrus.Errorf("failed to export tar file: %v", err) + os.Exit(1) + } + logrus.Info("tar export finished") + } } if tb != "" { logrus.Info("building [Overlaybd - Turbo OCIv1] image...") @@ -161,6 +300,16 @@ Version: ` + commitID, os.Exit(1) } logrus.Info("TurboOCIv1 build finished") + + // Handle tar export if requested + if exportTar != "" && exportResolver != nil { + logrus.Infof("exporting converted turboOCI layers to tar file: %s", exportTar) + if err := builder.ExportContentStoreToTar(ctx, exportResolver.OutputStore(), exportResolver.OutputImageStore(), exportTar); err != nil { + logrus.Errorf("failed to export tar file: %v", err) + os.Exit(1) + } + logrus.Info("tar export finished") + } } }, } @@ -189,6 +338,11 @@ func init() { rootCmd.Flags().BoolVar(&disableSparse, "disable-sparse", false, "disable sparse file for overlaybd") rootCmd.Flags().BoolVar(&referrer, "referrer", false, "push converted manifests with subject, note '--oci' will be enabled automatically if '--referrer' is set, cause the referrer must be in OCI format.") + // tar import/export + rootCmd.Flags().StringVar(&importTar, "import-tar", "", "import image from tar file (OCI layout format)") + rootCmd.Flags().StringVar(&exportTar, "export-tar", "", "export converted image to tar file (OCI layout format)") + rootCmd.Flags().StringVar(&tarExportRepo, "tar-export-repo", "localhost/converted", "repository name used in exported tar file (only used with --export-tar)") + // certification rootCmd.Flags().StringArrayVar(&certDirs, "cert-dir", nil, "In these directories, root CA should be named as *.crt and client cert should be named as *.cert, *.key") rootCmd.Flags().StringArrayVar(&rootCAs, "root-ca", nil, "root CA certificates") @@ -200,7 +354,8 @@ func init() { rootCmd.Flags().BoolVar(&noUpload, "no-upload", false, "don't upload layer and manifest") rootCmd.Flags().BoolVar(&dumpManifest, "dump-manifest", false, "dump manifest") - rootCmd.MarkFlagRequired("repository") + // Repository is required except when using tar import/export mode + // We'll validate this in the Run function instead } func main() { diff --git a/cmd/ctr/overlaybd_conv.go b/cmd/ctr/overlaybd_conv.go index 31bbeedc..9bfaf6a0 100644 --- a/cmd/ctr/overlaybd_conv.go +++ b/cmd/ctr/overlaybd_conv.go @@ -108,15 +108,19 @@ var convertCommand = &cli.Command{ fmt.Printf("vsize: %d GB\n", vsize) obdOpts = append(obdOpts, obdconv.WithVsize(vsize)) + fmt.Printf("Getting resolver...\n") resolver, err := commands.GetResolver(ctx, context) if err != nil { return err } + fmt.Printf("Resolver obtained successfully\n") + obdOpts = append(obdOpts, obdconv.WithResolver(resolver)) obdOpts = append(obdOpts, obdconv.WithImageRef(srcImage)) obdOpts = append(obdOpts, obdconv.WithClient(cli)) convertOpts = append(convertOpts, converter.WithIndexConvertFunc(obdconv.IndexConvertFunc(obdOpts...))) + fmt.Printf("Starting conversion...\n") newImg, err := converter.Convert(ctx, cli, targetImage, srcImage, convertOpts...) if err != nil { return err diff --git a/cmd/overlaybd-snapshotter/main.go b/cmd/overlaybd-snapshotter/main.go index 9c4ac4f0..2150a2e2 100644 --- a/cmd/overlaybd-snapshotter/main.go +++ b/cmd/overlaybd-snapshotter/main.go @@ -19,18 +19,22 @@ package main import ( "context" "encoding/json" + "math/rand" "net" "os" "os/signal" "path/filepath" "runtime" "strings" + "time" + mylog "github.com/containerd/accelerated-container-image/internal/log" "github.com/containerd/accelerated-container-image/pkg/metrics" overlaybd "github.com/containerd/accelerated-container-image/pkg/snapshot" snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1" "github.com/containerd/containerd/v2/contrib/snapshotservice" + "github.com/containerd/log" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -42,6 +46,16 @@ const defaultConfigPath = "/etc/overlaybd-snapshotter/config.json" var pconfig *overlaybd.BootConfig var commitID string = "unknown" +func requestIDInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + requestID := mylog.GenerateRequestID() + ctx = mylog.WithRequestID(ctx, requestID) + + // Add to containerd's logger context + ctx = log.WithLogger(ctx, log.G(ctx).WithField("req_id", requestID)) + + return handler(ctx, req) +} + func parseConfig(fpath string) error { logrus.Info("parse config file: ", fpath) data, err := os.ReadFile(fpath) @@ -104,7 +118,10 @@ func main() { } defer sn.Close() - srv := grpc.NewServer() + // Initialize random seed for request ID generation + rand.Seed(time.Now().UnixNano()) + + srv := grpc.NewServer(grpc.UnaryInterceptor(requestIDInterceptor)) snapshotsapi.RegisterSnapshotsServer(srv, snapshotservice.FromSnapshotter(sn)) address := strings.TrimSpace(pconfig.Address) diff --git a/go.mod b/go.mod index bce20376..63c092cc 100644 --- a/go.mod +++ b/go.mod @@ -1,106 +1,111 @@ module github.com/containerd/accelerated-container-image -go 1.22.0 +go 1.23.0 + +toolchain go1.23.1 require ( - github.com/containerd/containerd/api v1.8.0-rc.2 - github.com/containerd/containerd/v2 v2.0.0-rc.3 - github.com/containerd/continuity v0.4.3 - github.com/containerd/errdefs v0.1.0 - github.com/containerd/go-cni v1.1.9 + github.com/containerd/containerd/api v1.9.0 + github.com/containerd/containerd/v2 v2.1.3 + github.com/containerd/continuity v0.4.5 + github.com/containerd/errdefs v1.0.0 + github.com/containerd/go-cni v1.1.12 github.com/containerd/log v0.1.0 - github.com/containerd/platforms v0.2.1 + github.com/containerd/platforms v1.0.0-rc.1 github.com/data-accelerator/zdfs v0.1.5 github.com/docker/go-units v0.5.0 github.com/go-sql-driver/mysql v1.8.1 - github.com/jessevdk/go-flags v1.6.1 github.com/moby/locker v1.0.1 - github.com/moby/sys/mountinfo v0.7.1 + 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/opencontainers/image-spec v1.1.1 + github.com/opencontainers/runtime-spec v1.2.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.19.1 + github.com/prometheus/client_golang v1.22.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.1 - github.com/urfave/cli/v2 v2.27.2 - golang.org/x/sync v0.7.0 - golang.org/x/sys v0.21.0 - google.golang.org/grpc v1.63.2 + github.com/urfave/cli/v2 v2.27.6 + golang.org/x/sync v0.14.0 + golang.org/x/sys v0.33.0 + google.golang.org/grpc v1.72.2 oras.land/oras-go/v2 v2.5.0 ) require ( filippo.io/edwards25519 v1.1.0 // indirect - github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect - github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/Microsoft/hcsshim v0.12.4 // indirect + github.com/Microsoft/hcsshim v0.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cilium/ebpf v0.11.0 // indirect - github.com/containerd/cgroups/v3 v3.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cilium/ebpf v0.16.0 // indirect + github.com/containerd/cgroups/v3 v3.0.5 // indirect github.com/containerd/console v1.0.4 // 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 v0.1.0 // indirect - github.com/containerd/ttrpc v1.2.4 // indirect - github.com/containerd/typeurl/v2 v2.1.1 // indirect - github.com/containernetworking/cni v1.2.0 // indirect - github.com/containernetworking/plugins v1.4.1 // indirect + github.com/containerd/plugin v1.0.0 // 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/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // 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/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.1 // 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/stdr v1.2.2 // 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/google/go-cmp v0.6.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/intel/goresctrl v0.7.0 // indirect - github.com/klauspost/compress v1.17.8 // indirect - github.com/mdlayher/socket v0.4.1 // indirect + github.com/intel/goresctrl v0.8.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/mdlayher/socket v0.5.1 // indirect github.com/mdlayher/vsock v1.2.1 // indirect - github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/sys/signal v0.7.0 // indirect - github.com/moby/sys/symlink v0.2.0 // indirect - github.com/moby/sys/user v0.1.0 // indirect + 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.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // 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.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // 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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.9.0 // 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/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect - github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect - go.etcd.io/bbolt v1.3.10 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + go.etcd.io/bbolt v1.4.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect - go.opentelemetry.io/otel v1.26.0 // indirect - go.opentelemetry.io/otel/metric v1.26.0 // indirect - go.opentelemetry.io/otel/trace v1.26.0 // indirect - golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect - golang.org/x/mod v0.18.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect - google.golang.org/protobuf v1.34.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.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect + google.golang.org/protobuf v1.36.6 // 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/apimachinery v0.30.0 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect - tags.cncf.io/container-device-interface v0.7.2 // indirect - tags.cncf.io/container-device-interface/specs-go v0.7.0 // indirect + k8s.io/apimachinery v0.32.3 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect + tags.cncf.io/container-device-interface v1.0.1 // indirect + tags.cncf.io/container-device-interface/specs-go v1.0.0 // indirect ) // v0.5.2 was retagged: 71d70d4e738679154f62e2869e94784558cb9b36 -> fd022b910830015d8665a7b497f47bba1f90dd18 diff --git a/go.sum b/go.sum index bd145091..639cef9e 100644 --- a/go.sum +++ b/go.sum @@ -1,64 +1,63 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA= -github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU= +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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 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.4 h1:Ev7YUMHAHoWNm+aDSPzc5W9s6E2jyL1szpVDJeZ/Rr4= -github.com/Microsoft/hcsshim v0.12.4/go.mod h1:Iyl1WVpZzr+UkzjekHZbV8o5Z9ZkxNGx6CtY2Qg/JVQ= +github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA= +github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok= 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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.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/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.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/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.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/containerd/containerd/api v1.8.0-rc.2 h1:EnWLDKWWbIRzuy71L20P3VF/DhxSaDEocsovKPdW5Oo= -github.com/containerd/containerd/api v1.8.0-rc.2/go.mod h1:VgMSK19YOLolP4a1/b5vlVkTo8MzMoLPZnvD1PNWeGg= -github.com/containerd/containerd/v2 v2.0.0-rc.3 h1:rRISeKYnunLx8Byw8FQ/a62mTMtcr6ESGptS4+MwLaQ= -github.com/containerd/containerd/v2 v2.0.0-rc.3/go.mod h1:UBHR1DgWRQcEOINFkR94m0VC0MgKd3qg9LVPnudv9vs= -github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= -github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= -github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= +github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0= +github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI= +github.com/containerd/containerd/v2 v2.1.3 h1:eMD2SLcIQPdMlnlNF6fatlrlRLAeDaiGPGwmRKLZKNs= +github.com/containerd/containerd/v2 v2.1.3/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= +github.com/containerd/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.9 h1:ORi7P1dYzCwVM6XPN4n3CbkuOx/NZ2DOqy+SHRdo9rU= -github.com/containerd/go-cni v1.1.9/go.mod h1:XYrZJ1d5W6E2VOvjffL3IZq0Dz6bsVlERHbekNK90PM= +github.com/containerd/go-cni v1.1.12 h1:wm/5VD/i255hjM4uIZjBRiEQ7y98W9ACy/mHeLi4+94= +github.com/containerd/go-cni v1.1.12/go.mod h1:+jaqRBdtW5faJxj2Qwg1Of7GsV66xcvnCx4mSJtUlxU= github.com/containerd/go-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 v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= -github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -github.com/containerd/plugin v0.1.0 h1:CYMyZk9beRAIe1FEKItbMLLAz/z16aXrGc+B+nv0fU4= -github.com/containerd/plugin v0.1.0/go.mod h1:j6HlpMtkiZMgT4UsfVNxPBUkwdw9KQGU6nCLfRxnq+w= -github.com/containerd/ttrpc v1.2.4 h1:eQCQK4h9dxDmpOb9QOOMh2NHTfzroH1IkmHiKZi05Oo= -github.com/containerd/ttrpc v1.2.4/go.mod h1:ojvb8SJBSch0XkqNO0L0YX/5NxR3UnVk2LzFKBK0upc= -github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= -github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= -github.com/containernetworking/cni v1.2.0 h1:fEjhlfWwWAXEvlcMQu/i6z8DA0Kbu7EcmR5+zb6cm5I= -github.com/containernetworking/cni v1.2.0/go.mod h1:/r+vA/7vrynNfbvSP9g8tIKEoy6win7sALJAw4ZiJks= -github.com/containernetworking/plugins v1.4.1 h1:+sJRRv8PKhLkXIl6tH1D7RMi+CbbHutDGU+ErLBORWA= -github.com/containernetworking/plugins v1.4.1/go.mod h1:n6FFGKcaY4o2o5msgu/UImtoC+fpQXM3076VHfHbj60= +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.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.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +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/data-accelerator/zdfs v0.1.5 h1:F7td8AwicTZ3t618SsEYr8CMyMcxBE1KjkoyYO4T2GI= github.com/data-accelerator/zdfs v0.1.5/go.mod h1:/MyNTsQHHKVLznaRBz+PivhIDwglu+wuXKoZxUNmLKI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -75,19 +74,21 @@ 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/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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +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-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +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/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= @@ -107,102 +108,119 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 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= 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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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.5.9/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.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-20230323073829-e72429f035bd h1:r8yyd+DJDmsUhGrRBxH5Pj7KeFK5l+Y3FsgT8keqKtk= -github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= +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/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-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/intel/goresctrl v0.7.0 h1:x6RclP6LiJc24t9mf47BRbjf06B8oVisZMBv31x3rKc= -github.com/intel/goresctrl v0.7.0/go.mod h1:T3ZZnuHSNouwELB5wvOoUJaB7l/4Rm23rJy/wuWJlr0= -github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= -github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= +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/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/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.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +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/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.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g= -github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= -github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +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/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.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/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= -github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= -github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= -github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= +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/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.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= -github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +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.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= 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 v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +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/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 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.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/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -214,16 +232,17 @@ 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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -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.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= -github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= -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.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= +github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +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= @@ -231,37 +250,45 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo 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-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= -github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= +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.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= -go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= +go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= 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.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= -go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= -go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= -go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= -go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= -go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= -go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= +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.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.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.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 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= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -271,32 +298,30 @@ 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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.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-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.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.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/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= @@ -305,8 +330,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.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +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= @@ -316,15 +341,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-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= 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.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= +google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= 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= @@ -334,29 +359,26 @@ 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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +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/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/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.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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= -k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= +k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c= oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -tags.cncf.io/container-device-interface v0.7.2 h1:MLqGnWfOr1wB7m08ieI4YJ3IoLKKozEnnNYBtacDPQU= -tags.cncf.io/container-device-interface v0.7.2/go.mod h1:Xb1PvXv2BhfNb3tla4r9JL129ck1Lxv9KuU6eVOfKto= -tags.cncf.io/container-device-interface/specs-go v0.7.0 h1:w/maMGVeLP6TIQJVYT5pbqTi8SCw/iHZ+n4ignuGHqg= -tags.cncf.io/container-device-interface/specs-go v0.7.0/go.mod h1:hMAwAbMZyBLdmYqWgYcKH0F/yctNpV3P35f+/088A80= +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 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= diff --git a/internal/log/helper.go b/internal/log/helper.go index c6115bad..43cc66d3 100644 --- a/internal/log/helper.go +++ b/internal/log/helper.go @@ -19,10 +19,34 @@ package log import ( "context" "fmt" + "math/rand" clog "github.com/containerd/log" ) +type requestIDKey struct{} + +var chars = []rune("abcdefghijklmnopqrstuvwxyz0123456789") + +func GenerateRequestID() string { + b := make([]rune, 4) + for i := range b { + b[i] = chars[rand.Intn(len(chars))] + } + return string(b) +} + +func WithRequestID(ctx context.Context, id string) context.Context { + return context.WithValue(ctx, requestIDKey{}, id) +} + +func GetRequestID(ctx context.Context) string { + if id, ok := ctx.Value(requestIDKey{}).(string); ok { + return id + } + return "" +} + func TracedErrorf(ctx context.Context, format string, args ...any) error { err := fmt.Errorf(format, args...) clog.G(ctx).Error(err) diff --git a/pkg/convertor/convertor.go b/pkg/convertor/convertor.go index 9533c107..82b7e1a4 100644 --- a/pkg/convertor/convertor.go +++ b/pkg/convertor/convertor.go @@ -261,16 +261,21 @@ func NewOverlaybdConvertor(ctx context.Context, cs content.Store, sn snapshots.S } func (c *overlaybdConvertor) Convert(ctx context.Context, srcManifest ocispec.Manifest, fsType string) (ocispec.Descriptor, error) { + fmt.Printf("Convert: Reading config blob\n") configData, err := content.ReadBlob(ctx, c.cs, srcManifest.Config) if err != nil { + fmt.Printf("Convert: ERROR reading config blob: %v\n", err) return emptyDesc, err } + fmt.Printf("Convert: Unmarshaling image config\n") var srcCfg ocispec.Image if err := json.Unmarshal(configData, &srcCfg); err != nil { + fmt.Printf("Convert: ERROR unmarshaling config: %v\n", err) return emptyDesc, err } + fmt.Printf("Convert: Starting layer conversion for %d layers\n", len(srcManifest.Layers)) committedLayers, err := c.convertLayers(ctx, srcManifest.Layers, srcCfg.RootFS.DiffIDs, fsType) if err != nil { return emptyDesc, err @@ -461,6 +466,7 @@ func (c *overlaybdConvertor) sentToRemote(ctx context.Context, desc ocispec.Desc // convertLayers applys image layers on overlaybd with specified filesystem and // exports the layers based on zfile. func (c *overlaybdConvertor) convertLayers(ctx context.Context, srcDescs []ocispec.Descriptor, srcDiffIDs []digest.Digest, fsType string) ([]Layer, error) { + fmt.Printf("convertLayers: Starting conversion of %d layers\n", len(srcDescs)) var ( lastParentID string = "" err error @@ -482,17 +488,29 @@ func (c *overlaybdConvertor) convertLayers(ctx context.Context, srcDescs []ocisp eg, ctx := errgroup.WithContext(ctx) for idx, desc := range srcDescs { + // Skip provenance and attestation layers + if isProvenanceLayer(desc) { + log.G(ctx).Infof("Skipping provenance layer: %s", desc.MediaType) + continue + } + fmt.Printf("convertLayers: Processing layer %d/%d (digest: %s)\n", idx+1, len(srcDescs), desc.Digest) chain = append(chain, srcDiffIDs[idx]) chainID := identity.ChainID(chain).String() + fmt.Printf("convertLayers: Layer chainID: %s\n", chainID) var remoteDesc ocispec.Descriptor if c.remote { + fmt.Printf("convertLayers: Looking for remote layer\n") remoteDesc, err = c.findRemote(ctx, chainID) if err != nil { if !errdefs.IsNotFound(err) { + fmt.Printf("convertLayers: ERROR finding remote: %v\n", err) return nil, err } + fmt.Printf("convertLayers: Remote layer not found, will process locally\n") + } else { + fmt.Printf("convertLayers: Found remote layer\n") } } @@ -524,6 +542,7 @@ func (c *overlaybdConvertor) convertLayers(ctx context.Context, srcDescs []ocisp Annotations: map[string]string{ label.OverlayBDBlobDigest: remoteDesc.Digest.String(), label.OverlayBDBlobSize: fmt.Sprintf("%d", remoteDesc.Size), + label.OverlayBDBlobFsType: fsType, }, }, DiffID: remoteDesc.Digest, @@ -545,10 +564,13 @@ func (c *overlaybdConvertor) convertLayers(ctx context.Context, srcDescs []ocisp opts = append(opts, snapshots.WithLabels(map[string]string{ label.ZFileConfig: string(cfgStr), })) + fmt.Printf("convertLayers: Applying OCI layer %d to overlaybd\n", idx+1) lastParentID, err = c.applyOCIV1LayerInObd(ctx, lastParentID, desc, opts, nil) if err != nil { + fmt.Printf("convertLayers: ERROR applying layer %d: %v\n", idx+1, err) return nil, err } + fmt.Printf("convertLayers: Successfully applied layer %d, new snapshot ID: %s\n", idx+1, lastParentID) if c.remote { // must synchronize registry and db, can not do concurrently @@ -585,29 +607,37 @@ func (c *overlaybdConvertor) applyOCIV1LayerInObd( snOpts []snapshots.Opt, // apply for the commit snapshotter afterApply func(root string) error, // do something after apply tar stream ) (string, error) { + fmt.Printf("applyOCIV1LayerInObd: Starting layer application (digest: %s, size: %d)\n", desc.Digest, desc.Size) + fmt.Printf("applyOCIV1LayerInObd: Getting reader for layer content\n") ra, err := c.cs.ReaderAt(ctx, desc) if err != nil { + fmt.Printf("applyOCIV1LayerInObd: ERROR getting reader: %v\n", err) return emptyString, errors.Wrapf(err, "failed to get reader %s from content store", desc.Digest) } defer ra.Close() + fmt.Printf("applyOCIV1LayerInObd: Successfully got reader for layer content\n") var ( key string mounts []mount.Mount ) + fmt.Printf("applyOCIV1LayerInObd: Preparing snapshot (parentID: %s)\n", parentID) for { key = fmt.Sprintf(convSnapshotNameFormat, UniquePart()) + fmt.Printf("applyOCIV1LayerInObd: Attempting to prepare snapshot with key: %s\n", key) mounts, err = c.sn.Prepare(ctx, key, parentID, snOpts...) if err != nil { // retry other key if errdefs.IsAlreadyExists(err) { + fmt.Printf("applyOCIV1LayerInObd: Snapshot key already exists, retrying with new key\n") continue } + fmt.Printf("applyOCIV1LayerInObd: ERROR preparing snapshot: %v\n", err) return emptyString, errors.Wrapf(err, "failed to preprare snapshot %q", key) } - + fmt.Printf("applyOCIV1LayerInObd: Successfully prepared snapshot, got %d mounts\n", len(mounts)) break } @@ -748,6 +778,7 @@ func WithClient(client *containerd.Client) Option { func IndexConvertFunc(opts ...Option) converter.ConvertFunc { return func(ctx context.Context, cs content.Store, desc ocispec.Descriptor) (*ocispec.Descriptor, error) { + fmt.Printf("IndexConvertFunc: Starting conversion function\n") var copts options for _, o := range opts { if err := o(&copts); err != nil { @@ -756,25 +787,38 @@ func IndexConvertFunc(opts ...Option) converter.ConvertFunc { } client := copts.client imgRef := copts.imgRef + fmt.Printf("IndexConvertFunc: Getting snapshot service for overlaybd\n") sn := client.SnapshotService("overlaybd") + fmt.Printf("IndexConvertFunc: Getting source image: %s\n", imgRef) srcImg, err := client.GetImage(ctx, imgRef) if err != nil { + fmt.Printf("IndexConvertFunc: ERROR getting source image: %v\n", err) return nil, err } + fmt.Printf("IndexConvertFunc: Successfully got source image\n") + fmt.Printf("IndexConvertFunc: Reading manifest for image\n") + fmt.Printf("IndexConvertFunc: Image target digest: %s, mediaType: %s, size: %d\n", + srcImg.Target().Digest, srcImg.Target().MediaType, srcImg.Target().Size) + fmt.Printf("IndexConvertFunc: Using platform: %s\n", platforms.Default()) srcManifest, err := images.Manifest(ctx, cs, srcImg.Target(), platforms.Default()) if err != nil { + fmt.Printf("IndexConvertFunc: ERROR reading manifest: %v\n", err) return nil, errors.Wrapf(err, "failed to read manifest") } + fmt.Printf("IndexConvertFunc: Successfully read manifest with %d layers\n", len(srcManifest.Layers)) zfileCfg := ZFileConfig{ Algorithm: copts.algorithm, BlockSize: copts.blockSize, } + fmt.Printf("IndexConvertFunc: Creating OverlayBD convertor\n") c, err := NewOverlaybdConvertor(ctx, cs, sn, copts.resolver, imgRef, copts.dbstr, zfileCfg, copts.vsize) if err != nil { + fmt.Printf("IndexConvertFunc: ERROR creating convertor: %v\n", err) return nil, err } + fmt.Printf("IndexConvertFunc: Starting layer conversion with fsType: %s\n", copts.fsType) newMfstDesc, err := c.Convert(ctx, srcManifest, copts.fsType) if err != nil { return nil, err @@ -782,3 +826,43 @@ func IndexConvertFunc(opts ...Option) converter.ConvertFunc { return &newMfstDesc, nil } } + +// isProvenanceLayer checks if a layer descriptor represents provenance/attestation metadata +func isProvenanceLayer(desc ocispec.Descriptor) bool { + // Check for common provenance and attestation media types + provenanceTypes := []string{ + "application/vnd.in-toto+json", + "application/vnd.dev.cosign.simplesigning.v1+json", + "application/vnd.dev.sigstore.bundle+json", + "application/vnd.docker.distribution.manifest.v2+json", + } + + for _, pType := range provenanceTypes { + if strings.Contains(desc.MediaType, pType) { + return true + } + } + + // Check for provenance-related artifact types + if desc.ArtifactType != "" { + if strings.Contains(desc.ArtifactType, "provenance") || + strings.Contains(desc.ArtifactType, "attestation") || + strings.Contains(desc.ArtifactType, "signature") { + return true + } + } + + // Check annotations for provenance markers + if desc.Annotations != nil { + if _, exists := desc.Annotations["in-toto.io/predicate-type"]; exists { + return true + } + if _, exists := desc.Annotations["vnd.docker.reference.type"]; exists { + if refType := desc.Annotations["vnd.docker.reference.type"]; refType == "attestation-manifest" || strings.Contains(refType, "provenance") { + return true + } + } + } + + return false +} diff --git a/pkg/convertor/convertor_test.go b/pkg/convertor/convertor_test.go new file mode 100644 index 00000000..9b7193f4 --- /dev/null +++ b/pkg/convertor/convertor_test.go @@ -0,0 +1,209 @@ +/* + Copyright The Accelerated Container Image 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 convertor + +import ( + "testing" + + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +func TestIsProvenanceLayer(t *testing.T) { + tests := []struct { + name string + desc ocispec.Descriptor + expected bool + }{ + { + name: "in-toto attestation layer", + desc: ocispec.Descriptor{ + MediaType: "application/vnd.in-toto+json", + Digest: "sha256:abc123", + Size: 1024, + }, + expected: true, + }, + { + name: "cosign signature layer", + desc: ocispec.Descriptor{ + MediaType: "application/vnd.dev.cosign.simplesigning.v1+json", + Digest: "sha256:def456", + Size: 512, + }, + expected: true, + }, + { + name: "sigstore bundle layer", + desc: ocispec.Descriptor{ + MediaType: "application/vnd.dev.sigstore.bundle+json", + Digest: "sha256:ghi789", + Size: 256, + }, + expected: true, + }, + { + name: "layer with in-toto annotation", + desc: ocispec.Descriptor{ + MediaType: "application/vnd.oci.image.manifest.v1+json", + Digest: "sha256:stu901", + Size: 1024, + Annotations: map[string]string{ + "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2", + }, + }, + expected: true, + }, + { + name: "layer with docker attestation reference", + desc: ocispec.Descriptor{ + MediaType: "application/vnd.oci.image.manifest.v1+json", + Digest: "sha256:vwx234", + Size: 2048, + Annotations: map[string]string{ + "vnd.docker.reference.type": "attestation-manifest", + }, + }, + expected: true, + }, + { + name: "layer with provenance artifact type", + desc: ocispec.Descriptor{ + MediaType: "application/vnd.docker.distribution.manifest.v2+json", + ArtifactType: "application/vnd.example.provenance+json", + Digest: "sha256:jkl012", + Size: 2048, + }, + expected: true, + }, + { + name: "regular container layer", + desc: ocispec.Descriptor{ + MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", + Digest: "sha256:regular123", + Size: 5242880, + }, + expected: false, + }, + { + name: "oci layer", + desc: ocispec.Descriptor{ + MediaType: "application/vnd.oci.image.layer.v1.tar+gzip", + Digest: "sha256:oci456", + Size: 10485760, + }, + expected: false, + }, + { + name: "image config", + desc: ocispec.Descriptor{ + MediaType: "application/vnd.oci.image.config.v1+json", + Digest: "sha256:config789", + Size: 1024, + }, + expected: false, + }, + { + name: "empty descriptor", + desc: ocispec.Descriptor{}, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := isProvenanceLayer(tt.desc) + if result != tt.expected { + t.Errorf("isProvenanceLayer() = %v, expected %v for %s", result, tt.expected, tt.name) + } + }) + } +} + +// Test integration showing layer filtering behavior +func TestConvertLayersSkipProvenance(t *testing.T) { + // Create mock layer descriptors + layers := []ocispec.Descriptor{ + // Regular filesystem layer + { + MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", + Digest: "sha256:layer1", + Size: 5242880, + }, + // Provenance layer (should be skipped) + { + MediaType: "application/vnd.in-toto+json", + Digest: "sha256:provenance1", + Size: 2048, + Annotations: map[string]string{ + "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2", + }, + }, + // Another regular filesystem layer + { + MediaType: "application/vnd.oci.image.layer.v1.tar+gzip", + Digest: "sha256:layer2", + Size: 10485760, + }, + // Cosign signature layer (should be skipped) + { + MediaType: "application/vnd.dev.cosign.simplesigning.v1+json", + Digest: "sha256:signature1", + Size: 512, + }, + } + + // Count layers that would be processed vs skipped + filesystemLayers := 0 + provenanceLayers := 0 + + for _, layer := range layers { + if isProvenanceLayer(layer) { + provenanceLayers++ + } else { + filesystemLayers++ + } + } + + // Verify expected counts + expectedFilesystemLayers := 2 + expectedProvenanceLayers := 2 + + if filesystemLayers != expectedFilesystemLayers { + t.Errorf("Expected %d filesystem layers, got %d", expectedFilesystemLayers, filesystemLayers) + } + + if provenanceLayers != expectedProvenanceLayers { + t.Errorf("Expected %d provenance layers, got %d", expectedProvenanceLayers, provenanceLayers) + } + + // Verify specific layer classifications + if isProvenanceLayer(layers[0]) { + t.Errorf("Expected regular filesystem layer to NOT be identified as provenance layer") + } + + if !isProvenanceLayer(layers[1]) { + t.Errorf("Expected in-toto layer to be identified as provenance layer") + } + + if isProvenanceLayer(layers[2]) { + t.Errorf("Expected regular filesystem layer to NOT be identified as provenance layer") + } + + if !isProvenanceLayer(layers[3]) { + t.Errorf("Expected cosign layer to be identified as provenance layer") + } +} diff --git a/pkg/snapshot/overlay.go b/pkg/snapshot/overlay.go index 09ddde5b..e7c2afd6 100644 --- a/pkg/snapshot/overlay.go +++ b/pkg/snapshot/overlay.go @@ -51,25 +51,29 @@ import ( type storageType int const ( - // storageTypeUnknown is placeholder for unknown type. + // storageTypeUnknown is a placeholder for unidentified storage types. + // Used when file format detection fails or storage type cannot be determined. storageTypeUnknown storageType = iota - // storageTypeNormal means that the unpacked layer data is from tar.gz - // or other valid media type from OCI image spec. This kind of the - // snapshotter can be used as normal lowerdir layer of overlayFS. + // storageTypeNormal represents standard OCI image layers (tar.gz, etc.). + // Used for regular container images and pause containers that fall back to + // standard overlayFS operations without overlaybd optimizations. storageTypeNormal - // storageTypeLocalBlock means that the unpacked layer data is in - // Overlaybd format. + // storageTypeLocalBlock represents data in optimized overlaybd format. + // Provides faster layer access through local block device mounting. + // Can be read-only or writable depending on configuration. storageTypeLocalBlock - // storageTypeRemoteBlock means that there is no unpacked layer data. - // But there are labels to mark data that will be pulling on demand. + // storageTypeRemoteBlock represents remote overlaybd format with on-demand loading. + // No local unpacked data; uses remote pulling based on blob metadata labels. + // Optimized for fastest container startup without full image download. + // Always read-only. storageTypeRemoteBlock - // storageTypeLocalLayer means that the unpacked layer data is in - // a tar file, which needs to generate overlaybd-turboOCI meta before - // create an overlaybd device + // storageTypeLocalLayer represents uncompressed tar files needing conversion. + // Intermediate state before becoming storageTypeLocalBlock. + // Triggers conversion to turboOCI format during commit operation. storageTypeLocalLayer ) @@ -368,18 +372,17 @@ func (o *snapshotter) Usage(ctx context.Context, key string) (_ snapshots.Usage, } func (o *snapshotter) getWritableType(ctx context.Context, id string, info snapshots.Info) (mode string) { + // Extract image reference for better logging context + imageRef := "" + if img, ok := info.Labels[label.CRIImageRef]; ok { + imageRef = img + } else if img, ok := info.Labels[label.TargetImageRef]; ok { + imageRef = img + } + defer func() { - log.G(ctx).Infof("snapshot R/W label: %s", mode) + log.G(ctx).Infof("snapshot R/W mode selected: %s (image: %s, snapshotID: %s)", mode, imageRef, id) }() - // check image type (OCIv1 or overlaybd) - if id != "" { - if _, err := o.loadBackingStoreConfig(id); err != nil { - log.G(ctx).Debugf("[%s] is not an overlaybd image.", id) - return RoDir - } - } else { - log.G(ctx).Debugf("empty snID get. It should be an initial layer.") - } // overlaybd rwMode := func(m string) string { if m == "dir" { @@ -391,11 +394,36 @@ func (o *snapshotter) getWritableType(ctx context.Context, id string, info snaps return RoDir } m, ok := info.Labels[label.SupportReadWriteMode] - if !ok { - return rwMode(o.rwMode) + if ok { + log.G(ctx).Debugf("SupportReadWriteMode label found for snapshot %s: %s", id, m) + return rwMode(m) } - return rwMode(m) + if o.rwMode == "dev" { + // Check to see if this is an overlaybd snapshot based on labels or filesystem. + + // First, check for overlaybd blob metadata in labels + _, hasOverlayBDBlob := info.Labels[label.OverlayBDBlobDigest] + _, hasOverlayBDSize := info.Labels[label.OverlayBDBlobSize] + + if hasOverlayBDBlob && hasOverlayBDSize { + log.G(ctx).Debugf("OverlayBD labels detected for snapshot %s - using overlaybd (rwMode: %s)", id, o.rwMode) + return rwMode(o.rwMode) + } + // Fallback to checking the filesystem if labels are not present + stype, err := o.identifySnapshotStorageType(ctx, id, info) + if err == nil && (stype == storageTypeLocalBlock || stype == storageTypeRemoteBlock) { + log.G(ctx).Debugf("OverlayBD filesystem detected for snapshot %s - using overlaybd (rwMode: %s)", id, o.rwMode) + return rwMode(o.rwMode) + } + + // This appears to be a standard OCI layer. + log.G(ctx).Debugf("Fallback: No overlaybd labels or filesystem found for image %s (snapshot %s) - using overlayfs (RoDir)", imageRef, id) + return RoDir + } + + log.G(ctx).Debugf("Returning default rwMode for snapshot %s: %s", id, o.rwMode) + return rwMode(o.rwMode) } func (o *snapshotter) checkTurboOCI(labels map[string]string) (bool, string, string) { @@ -439,7 +467,31 @@ func (o *snapshotter) createMountPoint(ctx context.Context, kind snapshots.Kind, if err != nil { return nil, err } - log.G(ctx).Infof("sn: %s, labels:[%+v]", id, info.Labels) + // Extract image reference for logging + imageRef := "" + if img, ok := info.Labels[label.CRIImageRef]; ok { + imageRef = img + } else if img, ok := info.Labels[label.TargetImageRef]; ok { + imageRef = img + } + + log.G(ctx).Debugf("Creating snapshot %s for image: %s", id, imageRef) + + hasOverlayBDBlob := false + hasImageRef := false + + if _, ok := info.Labels[label.OverlayBDBlobDigest]; ok { + hasOverlayBDBlob = true + } + if _, ok := info.Labels[label.CRIImageRef]; ok { + hasImageRef = true + } + if _, ok := info.Labels[label.TargetImageRef]; ok { + hasImageRef = true + } + if hasOverlayBDBlob && !hasImageRef { + log.G(ctx).Warnf("Image %s (snapshot %s) has overlaybd blob metadata but missing image reference labels", imageRef, id) + } defer func() { // the transaction rollback makes created snapshot invalid, just clean it. if retErr != nil && rollback { @@ -551,18 +603,30 @@ func (o *snapshotter) createMountPoint(ctx context.Context, kind snapshots.Kind, if o.isPrepareRootfs(info) { log.G(ctx).Infof("Preparing rootfs(%s). writeType: %s", s.ID, writeType) - if writeType != RoDir { - stype = storageTypeLocalBlock - if err := o.constructOverlayBDSpec(ctx, key, true); err != nil { - log.G(ctx).Errorln(err.Error()) - return nil, err - } - } else if parent != "" { + if parent != "" { + // Inherit storage type from parent (this child layer cannot diverge from the parent's storage type). stype, err = o.identifySnapshotStorageType(ctx, parentID, parentInfo) if err != nil { return nil, err } } + + if writeType != RoDir { + if stype == storageTypeRemoteBlock || stype == storageTypeLocalBlock { + // If the parent is a remote or local block, use local block for the writable child layer. + log.G(ctx).Debugf("Using local block storage for writable snapshot %s (writeType: %s)", s.ID, writeType) + stype = storageTypeLocalBlock + if err := o.constructOverlayBDSpec(ctx, key, true); err != nil { + log.G(ctx).Errorln(err.Error()) + return nil, err + } + } else { + // Fallback to normal overlayfs if the parent is not an overlaybd layer. + stype = storageTypeNormal + log.G(ctx).Debugf("Parent snapshot '%s' (ID: %s) is not an overlaybd layer. Defaulting writable child snapshot '%s' (ID: %s) to normal overlayfs.", parent, parentID, key, s.ID) + } + } + switch stype { case storageTypeLocalBlock, storageTypeRemoteBlock: if parent != "" { @@ -645,6 +709,7 @@ func (o *snapshotter) createMountPoint(ctx context.Context, kind snapshots.Kind, switch stype { case storageTypeNormal: if _, ok := info.Labels[label.LayerToTurboOCI]; ok { + log.G(ctx).Debugf("turboOCI mount: id=%s", s.ID) m = []mount.Mount{ { Source: o.upperPath(s.ID), @@ -656,9 +721,11 @@ func (o *snapshotter) createMountPoint(ctx context.Context, kind snapshots.Kind, }, } } else { + log.G(ctx).Debugf("normal overlay mount: id=%s", s.ID) m = o.normalOverlayMount(s) } case storageTypeLocalBlock, storageTypeRemoteBlock: + log.G(ctx).Debugf("basedOnBlockDeviceMount: id=%s, writeType=%s", s.ID, writeType) m, err = o.basedOnBlockDeviceMount(ctx, s, writeType) if err != nil { return nil, errors.Wrapf(err, "%s", err.Error()) @@ -674,6 +741,28 @@ func (o *snapshotter) createMountPoint(ctx context.Context, kind snapshots.Kind, // Prepare creates an active snapshot identified by key descending from the provided parent. func (o *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) (_ []mount.Mount, retErr error) { log.G(ctx).Infof("Prepare (key: %s, parent: %s)", key, parent) + + // Extract labels from opts to see what's being passed to us. + var allLabels map[string]string + for _, opt := range opts { + var tempInfo snapshots.Info + if err := opt(&tempInfo); err == nil && tempInfo.Labels != nil { + if allLabels == nil { + allLabels = make(map[string]string) + } + for k, v := range tempInfo.Labels { + allLabels[k] = v + } + } + } + + // Log what we received from containerd + if len(allLabels) > 0 { + log.G(ctx).Debugf("Prepare: Received %d labels from containerd", len(allLabels)) + } else { + log.G(ctx).Debugf("Prepare: No labels received from containerd (opts count: %d)", len(opts)) + } + start := time.Now() defer func() { if retErr != nil { @@ -873,7 +962,8 @@ func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap // Firstly, try to convert an OCIv1 tarball to a turboOCI layer. // then change stype to 'storageTypeLocalBlock' which can make it attach a overlaybd block if stype == storageTypeLocalLayer { - log.G(ctx).Infof("convet a local blob to turboOCI layer (sn: %s)", id) + // PERFORMANCE WARNING: Converting local layer to turboOCI format + log.G(ctx).Warnf("Converting local blob to turboOCI layer (sn: %s). This is a heavyweight operation that processes the entire layer and may take significant time.", id) if err := o.constructOverlayBDSpec(ctx, name, false); err != nil { return errors.Wrapf(err, "failed to construct overlaybd config") } @@ -1368,22 +1458,44 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k log.G(ctx).Errorf("write imageRef '%s'. path: %s, err: %v", img, filepath.Join(path, "image_ref"), err) } } else { - log.G(ctx).Warnf("imageRef meta not found.") + for labelKey := range info.Labels { + if strings.Contains(labelKey, "overlaybd") { + log.G(ctx).Warnf("No image reference labels found for overlaybd snapshot %s (ID: %s) during creation.", info.Name, id) + break + } + } } return id, info, nil } func (o *snapshotter) identifySnapshotStorageType(ctx context.Context, id string, info snapshots.Info) (storageType, error) { + // Extract image reference for logging + imageRef := "" + if img, ok := info.Labels[label.CRIImageRef]; ok { + imageRef = img + } else if img, ok := info.Labels[label.TargetImageRef]; ok { + imageRef = img + } + if _, ok := info.Labels[label.TargetSnapshotRef]; ok { _, hasBDBlobSize := info.Labels[label.OverlayBDBlobSize] _, hasBDBlobDigest := info.Labels[label.OverlayBDBlobDigest] _, hasRef := info.Labels[label.TargetImageRef] _, hasCriRef := info.Labels[label.CRIImageRef] + log.G(ctx).Debugf("Storage type analysis for snapshot %s (image: %s): blob-size=%t, blob-digest=%t, target-ref=%t, cri-ref=%t", + id, imageRef, hasBDBlobSize, hasBDBlobDigest, hasRef, hasCriRef) + if hasBDBlobSize && hasBDBlobDigest { if hasRef || hasCriRef { return storageTypeRemoteBlock, nil + } else { + log.G(ctx).Warnf("Image %s (snapshot %s) has overlaybd blob metadata (blob-size=%t, blob-digest=%t) but missing critical image reference labels (target-ref=%t, cri-ref=%t).", + imageRef, id, hasBDBlobSize, hasBDBlobDigest, hasRef, hasCriRef) } + } else { + log.G(ctx).Debugf("Snapshot %s (image: %s) missing overlaybd blob metadata: blob-size=%t, blob-digest=%t", + id, imageRef, hasBDBlobSize, hasBDBlobDigest) } } @@ -1394,7 +1506,7 @@ func (o *snapshotter) identifySnapshotStorageType(ctx context.Context, id string if err == nil { return st, nil } - log.G(ctx).Debugf("failed to identify by %s, error %v, try to identify by writable_data", filePath, err) + log.G(ctx).Debugf("failed to identify by magic file %s, error %v, try to identify by sealed file/writable_data", filePath, err) // check sealed data file filePath = o.overlaybdSealedFilePath(id) @@ -1424,6 +1536,7 @@ func (o *snapshotter) identifySnapshotStorageType(ctx context.Context, id string log.G(ctx).Debugf("%s/config.v1.json found, return storageTypeRemoteBlock", id) return storageTypeRemoteBlock, nil } + log.G(ctx).Debugf("Falling back to normal storage type for snapshot %s", id) return storageTypeNormal, nil } @@ -1588,6 +1701,17 @@ func isOverlaybdFileHeader(header []byte) bool { magic0 := *(*uint64)(unsafe.Pointer(&header[0])) magic1 := *(*uint64)(unsafe.Pointer(&header[8])) magic2 := *(*uint64)(unsafe.Pointer(&header[16])) - return (magic0 == 281910587246170 && magic1 == 7384066304294679924 && magic2 == 7017278244700045632) || - (magic0 == 564050879402828 && magic1 == 5478704352671792741 && magic2 == 9993152565363659426) + + // Check for ZFile-based overlaybd format: "ZFile " + "tuji.yyf" + "@Alibaba" + // magic0: 281910587246170 represents "ZFile " (little-endian) + // magic1: 7384066304294679924 represents "tuji.yyf" (little-endian) + // magic2: 7017278244700045632 represents "@Alibaba" (little-endian) + zfileFormat := magic0 == 281910587246170 && magic1 == 7384066304294679924 && magic2 == 7017278244700045632 + + // Check for LSMT-based overlaybd format: "LSMT " + binary data + // magic0: 564050879402828 represents "LSMT " (little-endian) + // magic1 & magic2: additional format-specific binary data + lsmtFormat := magic0 == 564050879402828 && magic1 == 5478704352671792741 && magic2 == 9993152565363659426 + + return zfileFormat || lsmtFormat } diff --git a/pkg/snapshot/storage.go b/pkg/snapshot/storage.go index c1471f45..6e9f5f2d 100644 --- a/pkg/snapshot/storage.go +++ b/pkg/snapshot/storage.go @@ -254,12 +254,17 @@ func IsErofsFilesystem(path string) bool { // // TODO(fuweid): need to track the middle state if the process has been killed. func (o *snapshotter) attachAndMountBlockDevice(ctx context.Context, snID string, writable string, fsType string, mkfs bool) (retErr error) { + log.G(ctx).Debugf("Starting block device attachment for snapshot %s (writable: %s, fsType: %s)", snID, writable, fsType) log.G(ctx).Debugf("lookup device mountpoint(%s) if exists before attach.", snID) - if err := lookup(o.overlaybdMountpoint(snID)); err == nil { + mountPoint := o.overlaybdMountpoint(snID) + if err := lookup(mountPoint); err == nil { + log.G(ctx).Debugf("device mountpoint(%s) already exists, skip attach.", mountPoint) return nil + } else if err != nil && !errors.Is(err, os.ErrNotExist) { + log.G(ctx).Warnf("device mountpoint(%s) exists, but lookup failed: %v, continue to attach.", mountPoint, err) } else { - log.G(ctx).Infof(err.Error()) + log.G(ctx).Debugf("device mountpoint(%s) not exists, continue to attach.", mountPoint) } targetPath := o.overlaybdTargetPath(snID) @@ -427,7 +432,7 @@ func (o *snapshotter) attachAndMountBlockDevice(ctx context.Context, snID string } } args = append(args, device) - log.G(ctx).Infof("fs type: %s, mkfs options: %v", fstype, args) + log.G(ctx).Infof("Creating %s filesystem on %s (this may take a moment)", fstype, device) out, err := exec.CommandContext(ctx, "mkfs", args...).CombinedOutput() if err != nil { return errors.Wrapf(err, "failed to mkfs for dev %s: %s", device, out) @@ -479,6 +484,7 @@ func (o *snapshotter) attachAndMountBlockDevice(ctx context.Context, snID string } } } + log.G(ctx).Debugf("Completed block device attachment for snapshot %s", snID) return nil } } diff --git a/pkg/utils/provenance.go b/pkg/utils/provenance.go new file mode 100644 index 00000000..73eed8ed --- /dev/null +++ b/pkg/utils/provenance.go @@ -0,0 +1,62 @@ +/* + Copyright The Accelerated Container Image 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 utils + +import ( + "strings" + + v1 "github.com/opencontainers/image-spec/specs-go/v1" +) + +// IsProvenanceDescriptor checks if a descriptor represents provenance/attestation metadata +func IsProvenanceDescriptor(desc v1.Descriptor) bool { + // Check for common provenance and attestation media types + provenanceTypes := []string{ + "application/vnd.in-toto+json", + "application/vnd.dev.cosign.simplesigning.v1+json", + "application/vnd.dev.sigstore.bundle+json", + } + + for _, pType := range provenanceTypes { + if strings.Contains(desc.MediaType, pType) { + return true + } + } + + // Check for provenance-related artifact types + if desc.ArtifactType != "" { + if strings.Contains(desc.ArtifactType, "provenance") || + strings.Contains(desc.ArtifactType, "attestation") || + strings.Contains(desc.ArtifactType, "signature") { + return true + } + } + + // Check annotations for provenance markers + if desc.Annotations != nil { + if _, exists := desc.Annotations["in-toto.io/predicate-type"]; exists { + return true + } + if _, exists := desc.Annotations["vnd.docker.reference.type"]; exists { + if refType := desc.Annotations["vnd.docker.reference.type"]; refType == "attestation-manifest" || strings.Contains(refType, "provenance") { + return true + } + } + } + + return false +} \ No newline at end of file