Skip to content

Commit 0a5cb1d

Browse files
authored
Merge pull request #4352 from ChengyuZhu6/estargz
commit: Add support for converting writable layers to eStargz blobs
2 parents 1345f24 + 9fa9071 commit 0a5cb1d

File tree

7 files changed

+142
-49
lines changed

7 files changed

+142
-49
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

cmd/nerdctl/image/image_convert.go

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -237,37 +237,6 @@ func convertOptions(cmd *cobra.Command) (types.ImageConvertOptions, error) {
237237
return types.ImageConvertOptions{
238238
GOptions: globalOptions,
239239
Format: format,
240-
// #region estargz flags
241-
Estargz: estargz,
242-
EstargzRecordIn: estargzRecordIn,
243-
EstargzCompressionLevel: estargzCompressionLevel,
244-
EstargzChunkSize: estargzChunkSize,
245-
EstargzMinChunkSize: estargzMinChunkSize,
246-
EstargzExternalToc: estargzExternalTOC,
247-
EstargzKeepDiffID: estargzKeepDiffID,
248-
// #endregion
249-
// #region zstd flags
250-
Zstd: zstd,
251-
ZstdCompressionLevel: zstdCompressionLevel,
252-
// #endregion
253-
// #region zstd:chunked flags
254-
ZstdChunked: zstdchunked,
255-
ZstdChunkedCompressionLevel: zstdChunkedCompressionLevel,
256-
ZstdChunkedChunkSize: zstdChunkedChunkSize,
257-
ZstdChunkedRecordIn: zstdChunkedRecordIn,
258-
// #endregion
259-
// #region nydus flags
260-
Nydus: nydus,
261-
NydusBuilderPath: nydusBuilderPath,
262-
NydusWorkDir: nydusWorkDir,
263-
NydusPrefetchPatterns: nydusPrefetchPatterns,
264-
NydusCompressor: nydusCompressor,
265-
// #endregion
266-
// #region overlaybd flags
267-
Overlaybd: overlaybd,
268-
OverlayFsType: overlaybdFsType,
269-
OverlaydbDBStr: overlaybdDbstr,
270-
// #endregion
271240
// #region generic flags
272241
Uncompress: uncompress,
273242
Oci: oci,
@@ -276,6 +245,38 @@ func convertOptions(cmd *cobra.Command) (types.ImageConvertOptions, error) {
276245
Platforms: platforms,
277246
AllPlatforms: allPlatforms,
278247
// #endregion
248+
// Embed image format options
249+
EstargzOptions: types.EstargzOptions{
250+
Estargz: estargz,
251+
EstargzRecordIn: estargzRecordIn,
252+
EstargzCompressionLevel: estargzCompressionLevel,
253+
EstargzChunkSize: estargzChunkSize,
254+
EstargzMinChunkSize: estargzMinChunkSize,
255+
EstargzExternalToc: estargzExternalTOC,
256+
EstargzKeepDiffID: estargzKeepDiffID,
257+
},
258+
ZstdOptions: types.ZstdOptions{
259+
Zstd: zstd,
260+
ZstdCompressionLevel: zstdCompressionLevel,
261+
},
262+
ZstdChunkedOptions: types.ZstdChunkedOptions{
263+
ZstdChunked: zstdchunked,
264+
ZstdChunkedCompressionLevel: zstdChunkedCompressionLevel,
265+
ZstdChunkedChunkSize: zstdChunkedChunkSize,
266+
ZstdChunkedRecordIn: zstdChunkedRecordIn,
267+
},
268+
NydusOptions: types.NydusOptions{
269+
Nydus: nydus,
270+
NydusBuilderPath: nydusBuilderPath,
271+
NydusWorkDir: nydusWorkDir,
272+
NydusPrefetchPatterns: nydusPrefetchPatterns,
273+
NydusCompressor: nydusCompressor,
274+
},
275+
OverlaybdOptions: types.OverlaybdOptions{
276+
Overlaybd: overlaybd,
277+
OverlayFsType: overlaybdFsType,
278+
OverlaydbDBStr: overlaybdDbstr,
279+
},
279280
Stdout: cmd.OutOrStdout(),
280281
}, nil
281282
}

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/api/types/image_types.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,16 @@ type ImageConvertOptions struct {
6767
// Format the output using the given Go template, e.g, 'json'
6868
Format string
6969

70-
// #region estargz flags
70+
// Embed image format options
71+
EstargzOptions
72+
ZstdOptions
73+
ZstdChunkedOptions
74+
NydusOptions
75+
OverlaybdOptions
76+
}
77+
78+
// EstargzOptions contains eStargz conversion options
79+
type EstargzOptions struct {
7180
// Estargz convert legacy tar(.gz) layers to eStargz for lazy pulling. Should be used in conjunction with '--oci'
7281
Estargz bool
7382
// EstargzRecordIn read 'ctr-remote optimize --record-out=<FILE>' record file (EXPERIMENTAL)
@@ -82,16 +91,18 @@ type ImageConvertOptions struct {
8291
EstargzExternalToc bool
8392
// EstargzKeepDiffID convert to esgz without changing diffID (cannot be used in conjunction with '--estargz-record-in'. must be specified with '--estargz-external-toc')
8493
EstargzKeepDiffID bool
85-
// #endregion
94+
}
8695

87-
// #region zstd flags
96+
// ZstdOptions contains zstd conversion options
97+
type ZstdOptions struct {
8898
// Zstd convert legacy tar(.gz) layers to zstd. Should be used in conjunction with '--oci'
8999
Zstd bool
90100
// ZstdCompressionLevel zstd compression level
91101
ZstdCompressionLevel int
92-
// #endregion
102+
}
93103

94-
// #region zstd:chunked flags
104+
// ZstdChunkedOptions contains zstd:chunked conversion options
105+
type ZstdChunkedOptions struct {
95106
// ZstdChunked convert legacy tar(.gz) layers to zstd:chunked for lazy pulling. Should be used in conjunction with '--oci'
96107
ZstdChunked bool
97108
// ZstdChunkedCompressionLevel zstd compression level
@@ -100,9 +111,10 @@ type ImageConvertOptions struct {
100111
ZstdChunkedChunkSize int
101112
// ZstdChunkedRecordIn read 'ctr-remote optimize --record-out=<FILE>' record file (EXPERIMENTAL)
102113
ZstdChunkedRecordIn string
103-
// #endregion
114+
}
104115

105-
// #region nydus flags
116+
// NydusOptions contains nydus conversion options
117+
type NydusOptions struct {
106118
// Nydus convert legacy tar(.gz) layers to nydus for lazy pulling. Should be used in conjunction with '--oci'
107119
Nydus bool
108120
// NydusBuilderPath the nydus-image binary path, if unset, search in PATH environment
@@ -113,17 +125,16 @@ type ImageConvertOptions struct {
113125
NydusPrefetchPatterns string
114126
// NydusCompressor nydus blob compression algorithm, possible values: `none`, `lz4_block`, `zstd`, default is `lz4_block`
115127
NydusCompressor string
116-
// #endregion
128+
}
117129

118-
// #region overlaybd flags
130+
// OverlaybdOptions contains overlaybd conversion options
131+
type OverlaybdOptions struct {
119132
// Overlaybd convert tar.gz layers to overlaybd layers
120133
Overlaybd bool
121134
// OverlayFsType filesystem type for overlaybd
122135
OverlayFsType string
123136
// OverlaydbDBStr database config string for overlaybd
124137
OverlaydbDBStr string
125-
// #endregion
126-
127138
}
128139

129140
// ImageCryptOptions specifies options for `nerdctl image encrypt` and `nerdctl image decrypt`.

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)