Skip to content

Commit 410b0f2

Browse files
authored
Merge pull request containerd#4330 from ChengyuZhu6/commit-format
commit: Add an option to nerdctl commit command for media type selection
2 parents 1024760 + df145ce commit 410b0f2

File tree

5 files changed

+86
-15
lines changed

5 files changed

+86
-15
lines changed

cmd/nerdctl/container/container_commit.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func CommitCommand() *cobra.Command {
4343
cmd.Flags().StringArrayP("change", "c", nil, "Apply Dockerfile instruction to the created image (supported directives: [CMD, ENTRYPOINT])")
4444
cmd.Flags().BoolP("pause", "p", true, "Pause container during commit")
4545
cmd.Flags().StringP("compression", "", "gzip", "commit compression algorithm (zstd or gzip)")
46+
cmd.Flags().String("format", "docker", "Format of the committed image (docker or oci)")
4647
return cmd
4748
}
4849

@@ -76,6 +77,15 @@ func commitOptions(cmd *cobra.Command) (types.ContainerCommitOptions, error) {
7677
if com != string(types.Zstd) && com != string(types.Gzip) {
7778
return types.ContainerCommitOptions{}, errors.New("--compression param only supports zstd or gzip")
7879
}
80+
81+
format, err := cmd.Flags().GetString("format")
82+
if err != nil {
83+
return types.ContainerCommitOptions{}, err
84+
}
85+
if format != string(types.ImageFormatDocker) && format != string(types.ImageFormatOCI) {
86+
return types.ContainerCommitOptions{}, errors.New("--format param only supports docker or oci")
87+
}
88+
7989
return types.ContainerCommitOptions{
8090
Stdout: cmd.OutOrStdout(),
8191
GOptions: globalOptions,
@@ -84,6 +94,7 @@ func commitOptions(cmd *cobra.Command) (types.ContainerCommitOptions, error) {
8494
Pause: pause,
8595
Change: change,
8696
Compression: types.CompressionType(com),
97+
Format: types.ImageFormat(format),
8798
}, nil
8899
}
89100

docs/command-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,7 @@ Flags:
777777
- :whale: `-c, --change`: Apply Dockerfile instruction to the created image (supported directives: [CMD, ENTRYPOINT])
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)
780+
- :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)
780781

781782
## Image management
782783

pkg/api/types/container_types.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ type ContainerCreateOptions struct {
140140
OomKillDisable bool
141141
// OomScoreAdjChanged specifies whether the OOM preferences has been changed
142142
OomScoreAdjChanged bool
143-
// OomScoreAdj specifies the tune containers OOM preferences (-1000 to 1000, rootless: 100 to 1000)
143+
// OomScoreAdj specifies the tune container's OOM preferences (-1000 to 1000, rootless: 100 to 1000)
144144
OomScoreAdj int
145145
// PidsLimit specifies the tune container pids limit
146146
PidsLimit int64
@@ -394,6 +394,8 @@ type ContainerCommitOptions struct {
394394
Pause bool
395395
// Compression is set commit compression algorithm
396396
Compression CompressionType
397+
// Format specifies the image format for the committed image (docker or oci)
398+
Format ImageFormat
397399
}
398400

399401
type CompressionType string
@@ -403,6 +405,15 @@ const (
403405
Gzip CompressionType = "gzip"
404406
)
405407

408+
type ImageFormat string
409+
410+
const (
411+
// ImageFormatDocker uses Docker Schema2 media types for compatibility
412+
ImageFormatDocker ImageFormat = "docker"
413+
// ImageFormatOCI uses OCI Image Format media types
414+
ImageFormatOCI ImageFormat = "oci"
415+
)
416+
406417
// ContainerDiffOptions specifies options for `nerdctl (container) diff`.
407418
type ContainerDiffOptions struct {
408419
Stdout io.Writer

pkg/cmd/container/commit.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func Commit(ctx context.Context, client *containerd.Client, rawRef string, req s
5050
Pause: options.Pause,
5151
Changes: changes,
5252
Compression: options.Compression,
53+
Format: options.Format,
5354
}
5455

5556
walker := &containerwalker.ContainerWalker{

pkg/imgutil/commit/commit.go

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ type Opts struct {
6363
Pause bool
6464
Changes Changes
6565
Compression types.CompressionType
66+
Format types.ImageFormat
6667
}
6768

6869
var (
@@ -177,7 +178,7 @@ func Commit(ctx context.Context, client *containerd.Client, container containerd
177178
// Sync filesystem to make sure that all the data writes in container could be persisted to disk.
178179
Sync()
179180

180-
diffLayerDesc, diffID, err := createDiff(ctx, id, sn, client.ContentStore(), differ, opts.Compression)
181+
diffLayerDesc, diffID, err := createDiff(ctx, id, sn, client.ContentStore(), differ, opts.Compression, opts)
181182
if err != nil {
182183
return emptyDigest, fmt.Errorf("failed to export layer: %w", err)
183184
}
@@ -192,7 +193,7 @@ func Commit(ctx context.Context, client *containerd.Client, container containerd
192193
return emptyDigest, fmt.Errorf("failed to apply diff: %w", err)
193194
}
194195

195-
commitManifestDesc, configDigest, err := writeContentsForImage(ctx, snName, baseImg, imageConfig, diffLayerDesc)
196+
commitManifestDesc, configDigest, err := writeContentsForImage(ctx, snName, baseImg, imageConfig, diffLayerDesc, opts)
196197
if err != nil {
197198
return emptyDigest, err
198199
}
@@ -287,14 +288,29 @@ func generateCommitImageConfig(ctx context.Context, container containerd.Contain
287288
}
288289

289290
// writeContentsForImage will commit oci image config and manifest into containerd's content store.
290-
func writeContentsForImage(ctx context.Context, snName string, baseImg containerd.Image, newConfig ocispec.Image, diffLayerDesc ocispec.Descriptor) (ocispec.Descriptor, digest.Digest, error) {
291+
func writeContentsForImage(ctx context.Context, snName string, baseImg containerd.Image, newConfig ocispec.Image, diffLayerDesc ocispec.Descriptor, opts *Opts) (ocispec.Descriptor, digest.Digest, error) {
291292
newConfigJSON, err := json.Marshal(newConfig)
292293
if err != nil {
293294
return ocispec.Descriptor{}, emptyDigest, err
294295
}
295296

297+
// Select media types based on format choice
298+
var configMediaType, manifestMediaType string
299+
switch opts.Format {
300+
case types.ImageFormatOCI:
301+
configMediaType = ocispec.MediaTypeImageConfig
302+
manifestMediaType = ocispec.MediaTypeImageManifest
303+
case types.ImageFormatDocker:
304+
configMediaType = images.MediaTypeDockerSchema2Config
305+
manifestMediaType = images.MediaTypeDockerSchema2Manifest
306+
default:
307+
// Default to Docker Schema2 for compatibility
308+
configMediaType = images.MediaTypeDockerSchema2Config
309+
manifestMediaType = images.MediaTypeDockerSchema2Manifest
310+
}
311+
296312
configDesc := ocispec.Descriptor{
297-
MediaType: images.MediaTypeDockerSchema2Config,
313+
MediaType: configMediaType,
298314
Digest: digest.FromBytes(newConfigJSON),
299315
Size: int64(len(newConfigJSON)),
300316
}
@@ -310,7 +326,7 @@ func writeContentsForImage(ctx context.Context, snName string, baseImg container
310326
MediaType string `json:"mediaType,omitempty"`
311327
ocispec.Manifest
312328
}{
313-
MediaType: images.MediaTypeDockerSchema2Manifest,
329+
MediaType: manifestMediaType,
314330
Manifest: ocispec.Manifest{
315331
Versioned: specs.Versioned{
316332
SchemaVersion: 2,
@@ -326,7 +342,7 @@ func writeContentsForImage(ctx context.Context, snName string, baseImg container
326342
}
327343

328344
newMfstDesc := ocispec.Descriptor{
329-
MediaType: images.MediaTypeDockerSchema2Manifest,
345+
MediaType: manifestMediaType,
330346
Digest: digest.FromBytes(newMfstJSON),
331347
Size: int64(len(newMfstJSON)),
332348
}
@@ -357,14 +373,45 @@ func writeContentsForImage(ctx context.Context, snName string, baseImg container
357373
}
358374

359375
// createDiff creates a layer diff into containerd's content store.
360-
func createDiff(ctx context.Context, name string, sn snapshots.Snapshotter, cs content.Store, comparer diff.Comparer, compression types.CompressionType) (ocispec.Descriptor, digest.Digest, error) {
361-
opts := make([]diff.Opt, 0)
362-
mediaType := images.MediaTypeDockerSchema2LayerGzip
363-
if compression == types.Zstd {
364-
opts = append(opts, diff.WithMediaType(ocispec.MediaTypeImageLayerZstd))
365-
mediaType = images.MediaTypeDockerSchema2LayerZstd
366-
}
367-
newDesc, err := rootfs.CreateDiff(ctx, name, sn, comparer, opts...)
376+
func createDiff(ctx context.Context, name string, sn snapshots.Snapshotter, cs content.Store, comparer diff.Comparer, compression types.CompressionType, opts *Opts) (ocispec.Descriptor, digest.Digest, error) {
377+
diffOpts := make([]diff.Opt, 0)
378+
var mediaType string
379+
380+
// Select media type based on format and compression
381+
switch opts.Format {
382+
case types.ImageFormatOCI:
383+
// Use OCI media types
384+
switch compression {
385+
case types.Zstd:
386+
diffOpts = append(diffOpts, diff.WithMediaType(ocispec.MediaTypeImageLayerZstd))
387+
mediaType = ocispec.MediaTypeImageLayerZstd
388+
default:
389+
diffOpts = append(diffOpts, diff.WithMediaType(ocispec.MediaTypeImageLayerGzip))
390+
mediaType = ocispec.MediaTypeImageLayerGzip
391+
}
392+
case types.ImageFormatDocker:
393+
// Use Docker Schema2 media types for compatibility
394+
switch compression {
395+
case types.Zstd:
396+
diffOpts = append(diffOpts, diff.WithMediaType(ocispec.MediaTypeImageLayerZstd))
397+
mediaType = images.MediaTypeDockerSchema2LayerZstd
398+
default:
399+
diffOpts = append(diffOpts, diff.WithMediaType(ocispec.MediaTypeImageLayerGzip))
400+
mediaType = images.MediaTypeDockerSchema2LayerGzip
401+
}
402+
default:
403+
// Default to Docker Schema2 media types for compatibility
404+
switch compression {
405+
case types.Zstd:
406+
diffOpts = append(diffOpts, diff.WithMediaType(ocispec.MediaTypeImageLayerZstd))
407+
mediaType = images.MediaTypeDockerSchema2LayerZstd
408+
default:
409+
diffOpts = append(diffOpts, diff.WithMediaType(ocispec.MediaTypeImageLayerGzip))
410+
mediaType = images.MediaTypeDockerSchema2LayerGzip
411+
}
412+
}
413+
414+
newDesc, err := rootfs.CreateDiff(ctx, name, sn, comparer, diffOpts...)
368415
if err != nil {
369416
return ocispec.Descriptor{}, digest.Digest(""), err
370417
}

0 commit comments

Comments
 (0)