Skip to content

Commit eaa86d3

Browse files
committed
feat: add igzip/pigz compression-variant
1 parent e2c5ffd commit eaa86d3

File tree

4 files changed

+70
-6
lines changed

4 files changed

+70
-6
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ Keys supported by image output:
289289
* `dangling-name-prefix=<value>`: name image with `prefix@<digest>`, used for anonymous images
290290
* `name-canonical=true`: add additional canonical name `name@<digest>`
291291
* `compression=<uncompressed|gzip|estargz|zstd>`: choose compression type for layers newly created and cached, gzip is default value. estargz should be used with `oci-mediatypes=true`.
292+
* `compression-variant=<gzip|igzip|pigz>`: choose different compression method for gzip, only worked with `compression=gzip`.
292293
* `compression-level=<value>`: compression level for gzip, estargz (0-9) and zstd (0-22)
293294
* `rewrite-timestamp=true`: rewrite the file timestamps to the `SOURCE_DATE_EPOCH` value.
294295
See [`docs/build-repro.md`](docs/build-repro.md) for how to specify the `SOURCE_DATE_EPOCH` value.
@@ -467,6 +468,7 @@ buildctl build ... \
467468
* `image-manifest=<true|false>`: whether to export cache manifest as an OCI-compatible image manifest rather than a manifest list/index (default: `true` since BuildKit `v0.21`, must be used with `oci-mediatypes=true`)
468469
* `oci-mediatypes=<true|false>`: whether to use OCI mediatypes in exported manifests (default: `true`, since BuildKit `v0.8`)
469470
* `compression=<uncompressed|gzip|estargz|zstd>`: choose compression type for layers newly created and cached, gzip is default value. estargz and zstd should be used with `oci-mediatypes=true`
471+
* `compression-variant=<gzip|igzip|pigz>`: choose different compression method for gzip, only worked with `compression=gzip`.
470472
* `compression-level=<value>`: choose compression level for gzip, estargz (0-9) and zstd (0-22)
471473
* `force-compression=true`: forcibly apply `compression` option to all layers
472474
* `ignore-error=<false|true>`: specify if error is ignored in case cache export fails (default: `false`)
@@ -494,6 +496,7 @@ The directory layout conforms to OCI Image Spec v1.0.
494496
* `image-manifest=<true|false>`: whether to export cache manifest as an OCI-compatible image manifest rather than a manifest list/index (default: `true` since BuildKit `v0.21`, must be used with `oci-mediatypes=true`)
495497
* `oci-mediatypes=<true|false>`: whether to use OCI mediatypes in exported manifests (default `true`, since BuildKit `v0.8`)
496498
* `compression=<uncompressed|gzip|estargz|zstd>`: choose compression type for layers newly created and cached, gzip is default value. estargz and zstd should be used with `oci-mediatypes=true`.
499+
* `compression-variant=<gzip|igzip|pigz>`: choose different compression method for gzip, only worked with `compression=gzip`.
497500
* `compression-level=<value>`: compression level for gzip, estargz (0-9) and zstd (0-22)
498501
* `force-compression=true`: forcibly apply `compression` option to all layers
499502
* `ignore-error=<false|true>`: specify if error is ignored in case cache export fails (default: `false`)

util/compression/attrs.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import (
77
)
88

99
const (
10-
attrLayerCompression = "compression"
11-
attrForceCompression = "force-compression"
12-
attrCompressionLevel = "compression-level"
10+
attrLayerCompression = "compression"
11+
attrForceCompression = "force-compression"
12+
attrCompressionLevel = "compression-level"
13+
attrCompressionVariant = "compression-variant"
1314
)
1415

1516
func ParseAttributes(attrs map[string]string) (Config, error) {
@@ -44,5 +45,8 @@ func ParseAttributes(attrs map[string]string) (Config, error) {
4445
}
4546
compressionConfig = compressionConfig.SetLevel(int(ii))
4647
}
48+
if v, ok := attrs[attrCompressionVariant]; ok {
49+
compressionConfig = compressionConfig.SetVariant(v)
50+
}
4751
return compressionConfig, nil
4852
}

util/compression/compression.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ var (
5454
)
5555

5656
type Config struct {
57-
Type Type
58-
Force bool
59-
Level *int
57+
Type Type
58+
Force bool
59+
Level *int
60+
Variant string
6061
}
6162

6263
func New(t Type) Config {
@@ -75,6 +76,11 @@ func (c Config) SetLevel(l int) Config {
7576
return c
7677
}
7778

79+
func (c Config) SetVariant(v string) Config {
80+
c.Variant = v
81+
return c
82+
}
83+
7884
const (
7985
mediaTypeDockerSchema2LayerZstd = images.MediaTypeDockerSchema2Layer + ".zstd"
8086
)

util/compression/gzip.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package compression
22

33
import (
4+
"bytes"
45
"compress/gzip"
56
"context"
7+
"errors"
8+
"fmt"
69
"io"
10+
"os/exec"
711

812
"github.com/containerd/containerd/v2/core/content"
913
"github.com/containerd/containerd/v2/core/images"
@@ -12,6 +16,12 @@ import (
1216

1317
func (c gzipType) Compress(ctx context.Context, comp Config) (compressorFunc Compressor, finalize Finalizer) {
1418
return func(dest io.Writer, _ string) (io.WriteCloser, error) {
19+
switch comp.Variant {
20+
case "igzip":
21+
return gzipCmdWriter(ctx, "igzip", comp)(dest)
22+
case "pigz":
23+
return gzipCmdWriter(ctx, "pigz", comp)(dest)
24+
}
1525
return gzipWriter(comp)(dest)
1626
}, nil
1727
}
@@ -64,3 +74,44 @@ func gzipWriter(comp Config) func(io.Writer) (io.WriteCloser, error) {
6474
return gzip.NewWriterLevel(dest, level)
6575
}
6676
}
77+
78+
type writeCloserWrapper struct {
79+
io.Writer
80+
closer func() error
81+
}
82+
83+
func (w *writeCloserWrapper) Close() error {
84+
return w.closer()
85+
}
86+
87+
func gzipCmdWriter(ctx context.Context, cmd string, comp Config) func(io.Writer) (io.WriteCloser, error) {
88+
return func(dest io.Writer) (io.WriteCloser, error) {
89+
reader, writer := io.Pipe()
90+
args := []string{"-c"}
91+
if comp.Level != nil {
92+
args = append(args, fmt.Sprintf("-%d", *comp.Level))
93+
}
94+
command := exec.CommandContext(ctx, cmd, args...)
95+
command.Stdin = reader
96+
command.Stdout = dest
97+
98+
var errBuf bytes.Buffer
99+
command.Stderr = &errBuf
100+
101+
if err := command.Start(); err != nil {
102+
return nil, err
103+
}
104+
105+
return &writeCloserWrapper{
106+
Writer: writer,
107+
closer: func() error {
108+
closeErr := writer.Close()
109+
waitErr := command.Wait()
110+
if waitErr != nil {
111+
return fmt.Errorf("%s: %s", waitErr, errBuf.String())
112+
}
113+
return errors.Join(closeErr, waitErr)
114+
},
115+
}, nil
116+
}
117+
}

0 commit comments

Comments
 (0)