Skip to content

Commit 9fa9071

Browse files
committed
commit: support estargz conversion with writable layer in container commit
support estargz conversion with writable layer in container commit Fixes: #4351 Signed-off-by: ChengyuZhu6 <[email protected]>
1 parent fe8d6af commit 9fa9071

File tree

5 files changed

+88
-7
lines changed

5 files changed

+88
-7
lines changed

cmd/nerdctl/container/container_commit.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ func CommitCommand() *cobra.Command {
4444
cmd.Flags().BoolP("pause", "p", true, "Pause container during commit")
4545
cmd.Flags().StringP("compression", "", "gzip", "commit compression algorithm (zstd or gzip)")
4646
cmd.Flags().String("format", "docker", "Format of the committed image (docker or oci)")
47+
cmd.Flags().Bool("estargz", false, "Convert the committed layer to eStargz for lazy pulling")
48+
cmd.Flags().Int("estargz-compression-level", 9, "eStargz compression level (1-9)")
49+
cmd.Flags().Int("estargz-chunk-size", 0, "eStargz chunk size")
50+
cmd.Flags().Int("estargz-min-chunk-size", 0, "The minimal number of bytes of data must be written in one gzip stream")
4751
return cmd
4852
}
4953

@@ -86,6 +90,23 @@ func commitOptions(cmd *cobra.Command) (types.ContainerCommitOptions, error) {
8690
return types.ContainerCommitOptions{}, errors.New("--format param only supports docker or oci")
8791
}
8892

93+
estargz, err := cmd.Flags().GetBool("estargz")
94+
if err != nil {
95+
return types.ContainerCommitOptions{}, err
96+
}
97+
estargzCompressionLevel, err := cmd.Flags().GetInt("estargz-compression-level")
98+
if err != nil {
99+
return types.ContainerCommitOptions{}, err
100+
}
101+
estargzChunkSize, err := cmd.Flags().GetInt("estargz-chunk-size")
102+
if err != nil {
103+
return types.ContainerCommitOptions{}, err
104+
}
105+
estargzMinChunkSize, err := cmd.Flags().GetInt("estargz-min-chunk-size")
106+
if err != nil {
107+
return types.ContainerCommitOptions{}, err
108+
}
109+
89110
return types.ContainerCommitOptions{
90111
Stdout: cmd.OutOrStdout(),
91112
GOptions: globalOptions,
@@ -95,6 +116,12 @@ func commitOptions(cmd *cobra.Command) (types.ContainerCommitOptions, error) {
95116
Change: change,
96117
Compression: types.CompressionType(com),
97118
Format: types.ImageFormat(format),
119+
EstargzOptions: types.EstargzOptions{
120+
Estargz: estargz,
121+
EstargzCompressionLevel: estargzCompressionLevel,
122+
EstargzChunkSize: estargzChunkSize,
123+
EstargzMinChunkSize: estargzMinChunkSize,
124+
},
98125
}, nil
99126
}
100127

docs/command-reference.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,10 @@ Flags:
778778
- :whale: `-p, --pause`: Pause container during commit (default: true)
779779
- :nerd_face: `--compression`: Commit compression algorithm (supported values: zstd or gzip) (default: gzip) (zstd is generally better for compression ratio but might not be as widely supported)
780780
- :nerd_face: `--format`: Format of the committed image (supported values: docker or oci) (default: docker) (docker uses Docker Schema2 media types for compatibility, oci uses OCI image format media types)
781+
- :nerd_face: `--estargz`: Convert the committed layer to eStargz for lazy pulling
782+
- :nerd_face: `--estargz-compression-level`: eStargz compression level (1-9) (default: 9)
783+
- :nerd_face: `--estargz-chunk-size`: eStargz chunk size
784+
- :nerd_face: `--estargz-min-chunk-size`: The minimal number of bytes of data must be written in one gzip stream
781785

782786
## Image management
783787

pkg/api/types/container_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,8 @@ type ContainerCommitOptions struct {
396396
Compression CompressionType
397397
// Format specifies the image format for the committed image (docker or oci)
398398
Format ImageFormat
399+
// Embed EstargzOptions for eStargz conversion options
400+
EstargzOptions
399401
}
400402

401403
type CompressionType string

pkg/cmd/container/commit.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ func Commit(ctx context.Context, client *containerd.Client, rawRef string, req s
4444
}
4545

4646
opts := &commit.Opts{
47-
Author: options.Author,
48-
Message: options.Message,
49-
Ref: parsedReference.String(),
50-
Pause: options.Pause,
51-
Changes: changes,
52-
Compression: options.Compression,
53-
Format: options.Format,
47+
Author: options.Author,
48+
Message: options.Message,
49+
Ref: parsedReference.String(),
50+
Pause: options.Pause,
51+
Changes: changes,
52+
Compression: options.Compression,
53+
Format: options.Format,
54+
EstargzOptions: options.EstargzOptions,
5455
}
5556

5657
walker := &containerwalker.ContainerWalker{

pkg/imgutil/commit/commit.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import (
4343
"github.com/containerd/errdefs"
4444
"github.com/containerd/log"
4545
"github.com/containerd/platforms"
46+
"github.com/containerd/stargz-snapshotter/estargz"
47+
estargzconvert "github.com/containerd/stargz-snapshotter/nativeconverter/estargz"
4648

4749
"github.com/containerd/nerdctl/v2/pkg/api/types"
4850
"github.com/containerd/nerdctl/v2/pkg/clientutil"
@@ -64,6 +66,7 @@ type Opts struct {
6466
Changes Changes
6567
Compression types.CompressionType
6668
Format types.ImageFormat
69+
types.EstargzOptions
6770
}
6871

6972
var (
@@ -431,6 +434,50 @@ func createDiff(ctx context.Context, name string, sn snapshots.Snapshotter, cs c
431434
return ocispec.Descriptor{}, digest.Digest(""), err
432435
}
433436

437+
// Convert to eStargz if requested
438+
if opts.Estargz {
439+
log.G(ctx).Infof("Converting diff layer to eStargz format")
440+
441+
esgzOpts := []estargz.Option{
442+
estargz.WithCompressionLevel(opts.EstargzCompressionLevel),
443+
}
444+
if opts.EstargzChunkSize > 0 {
445+
esgzOpts = append(esgzOpts, estargz.WithChunkSize(opts.EstargzChunkSize))
446+
}
447+
if opts.EstargzMinChunkSize > 0 {
448+
esgzOpts = append(esgzOpts, estargz.WithMinChunkSize(opts.EstargzMinChunkSize))
449+
}
450+
451+
convertFunc := estargzconvert.LayerConvertFunc(esgzOpts...)
452+
453+
esgzDesc, err := convertFunc(ctx, cs, newDesc)
454+
if err != nil {
455+
return ocispec.Descriptor{}, digest.Digest(""), fmt.Errorf("failed to convert diff layer to eStargz: %w", err)
456+
} else if esgzDesc != nil {
457+
esgzDesc.MediaType = mediaType
458+
esgzInfo, err := cs.Info(ctx, esgzDesc.Digest)
459+
if err != nil {
460+
return ocispec.Descriptor{}, digest.Digest(""), err
461+
}
462+
463+
esgzDiffIDStr, ok := esgzInfo.Labels["containerd.io/uncompressed"]
464+
if !ok {
465+
return ocispec.Descriptor{}, digest.Digest(""), fmt.Errorf("invalid differ response with no diffID")
466+
}
467+
468+
esgzDiffID, err := digest.Parse(esgzDiffIDStr)
469+
if err != nil {
470+
return ocispec.Descriptor{}, digest.Digest(""), err
471+
}
472+
return ocispec.Descriptor{
473+
MediaType: esgzDesc.MediaType,
474+
Digest: esgzDesc.Digest,
475+
Size: esgzDesc.Size,
476+
Annotations: esgzDesc.Annotations,
477+
}, esgzDiffID, nil
478+
}
479+
}
480+
434481
return ocispec.Descriptor{
435482
MediaType: mediaType,
436483
Digest: newDesc.Digest,

0 commit comments

Comments
 (0)