Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions util/compression/compression.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type (
gzipType struct{}
estargzType struct{}
zstdType struct{}
zstdChunkedType struct{}
)

var (
Expand All @@ -51,6 +52,13 @@ var (

// Zstd is used for Zstandard data.
Zstd = zstdType{}

// ZstdChunked is used for zstd:chunked data, where each file in the
// tar stream is compressed independently with a TOC appended as a
// zstd skippable frame. This enables partial/lazy pulling by
// consumers that understand the format (e.g. podman/containers-storage).
// The resulting blob is backwards-compatible with standard zstd.
ZstdChunked = zstdChunkedType{}
)

type Config struct {
Expand Down Expand Up @@ -91,6 +99,8 @@ func parse(t string) (Type, error) {
return EStargz, nil
case Zstd.String():
return Zstd, nil
case ZstdChunked.String():
return ZstdChunked, nil
default:
return nil, errors.Errorf("unsupported compression type %s", t)
}
Expand Down
63 changes: 63 additions & 0 deletions util/compression/zstdchunked.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package compression

import (
"context"
"io"

"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/images"
chunkedcompressor "github.com/containers/storage/pkg/chunked/compressor"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like we consume an unrelated package just for zstd compression for a module not directly aimed at compression. If anything I think it should be either vendored here with tests or use a proper module. Also github.com/containers/storage is deprecated and has moved to https://github.com/containers/container-libs/tree/main/storage.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also vendor is not updated in your PR (go mod vendor).

ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
)

func (c zstdChunkedType) Compress(ctx context.Context, comp Config) (compressorFunc Compressor, finalize Finalizer) {
return func(dest io.Writer, _ string) (io.WriteCloser, error) {
level := 3
if comp.Level != nil {
level = *comp.Level
}
return chunkedcompressor.ZstdCompressor(dest, nil, &level)
}, nil
}

func (c zstdChunkedType) Decompress(ctx context.Context, cs content.Store, desc ocispecs.Descriptor) (io.ReadCloser, error) {
// zstd:chunked is a valid zstd stream — standard decompression works.
return decompress(ctx, cs, desc)
}

func (c zstdChunkedType) NeedsConversion(ctx context.Context, cs content.Store, desc ocispecs.Descriptor) (bool, error) {
if !images.IsLayerType(desc.MediaType) {
return false, nil
}
ct, err := FromMediaType(desc.MediaType)
if err != nil {
return false, err
}
// zstd:chunked uses the same media type as zstd, so if the layer is
// already zstd we skip conversion. The chunked structure is an
// optimization; re-encoding an existing zstd layer would require
// full decompression and recompression.
if ct == Zstd {
return false, nil
}
return true, nil
}

func (c zstdChunkedType) NeedsComputeDiffBySelf(comp Config) bool {
return true
}

func (c zstdChunkedType) OnlySupportOCITypes() bool {
return false
}

func (c zstdChunkedType) MediaType() string {
// zstd:chunked uses the same OCI media type as zstd — the chunked
// structure is transparent to registries and runtimes that don't
// understand it. They simply decompress it as a regular zstd blob.
return ocispecs.MediaTypeImageLayerZstd
}

func (c zstdChunkedType) String() string {
return "zstd:chunked"
}