github.com/moby/docker@v26.1.3+incompatible/daemon/images/image_commit.go (about)

     1  package images // import "github.com/docker/docker/daemon/images"
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"io"
     7  
     8  	"github.com/docker/docker/api/types/backend"
     9  	"github.com/docker/docker/image"
    10  	"github.com/docker/docker/layer"
    11  	"github.com/docker/docker/pkg/ioutils"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // CommitImage creates a new image from a commit config
    16  func (i *ImageService) CommitImage(ctx context.Context, c backend.CommitConfig) (image.ID, error) {
    17  	if err := ctx.Err(); err != nil {
    18  		return "", err
    19  	}
    20  
    21  	rwTar, err := exportContainerRw(i.layerStore, c.ContainerID, c.ContainerMountLabel)
    22  	if err != nil {
    23  		return "", err
    24  	}
    25  	defer func() {
    26  		if rwTar != nil {
    27  			rwTar.Close()
    28  		}
    29  	}()
    30  
    31  	var parent *image.Image
    32  	if c.ParentImageID == "" {
    33  		parent = new(image.Image)
    34  		parent.RootFS = image.NewRootFS()
    35  	} else {
    36  		parent, err = i.imageStore.Get(image.ID(c.ParentImageID))
    37  		if err != nil {
    38  			return "", err
    39  		}
    40  	}
    41  
    42  	l, err := i.layerStore.Register(rwTar, parent.RootFS.ChainID())
    43  	if err != nil {
    44  		return "", err
    45  	}
    46  	defer layer.ReleaseAndLog(i.layerStore, l)
    47  
    48  	cc := image.ChildConfig{
    49  		ContainerID:     c.ContainerID,
    50  		Author:          c.Author,
    51  		Comment:         c.Comment,
    52  		ContainerConfig: c.ContainerConfig,
    53  		Config:          c.Config,
    54  		DiffID:          l.DiffID(),
    55  	}
    56  	config, err := json.Marshal(image.NewChildImage(parent, cc, c.ContainerOS))
    57  	if err != nil {
    58  		return "", err
    59  	}
    60  
    61  	id, err := i.imageStore.Create(config)
    62  	if err != nil {
    63  		return "", err
    64  	}
    65  	if err := i.imageStore.SetBuiltLocally(id); err != nil {
    66  		return "", err
    67  	}
    68  
    69  	if c.ParentImageID != "" {
    70  		if err := i.imageStore.SetParent(id, image.ID(c.ParentImageID)); err != nil {
    71  			return "", err
    72  		}
    73  	}
    74  	return id, nil
    75  }
    76  
    77  func exportContainerRw(layerStore layer.Store, id, mountLabel string) (arch io.ReadCloser, err error) {
    78  	rwlayer, err := layerStore.GetRWLayer(id)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	defer func() {
    83  		if err != nil {
    84  			layerStore.ReleaseRWLayer(rwlayer)
    85  		}
    86  	}()
    87  
    88  	// TODO: this mount call is not necessary as we assume that TarStream() should
    89  	// mount the layer if needed. But the Diff() function for windows requests that
    90  	// the layer should be mounted when calling it. So we reserve this mount call
    91  	// until windows driver can implement Diff() interface correctly.
    92  	_, err = rwlayer.Mount(mountLabel)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	archive, err := rwlayer.TarStream()
    98  	if err != nil {
    99  		rwlayer.Unmount()
   100  		return nil, err
   101  	}
   102  	return ioutils.NewReadCloserWrapper(archive, func() error {
   103  			archive.Close()
   104  			err = rwlayer.Unmount()
   105  			layerStore.ReleaseRWLayer(rwlayer)
   106  			return err
   107  		}),
   108  		nil
   109  }
   110  
   111  // CommitBuildStep is used by the builder to create an image for each step in
   112  // the build.
   113  //
   114  // This method is different from CreateImageFromContainer:
   115  //   - it doesn't attempt to validate container state
   116  //   - it doesn't send a commit action to metrics
   117  //   - it doesn't log a container commit event
   118  //
   119  // This is a temporary shim. Should be removed when builder stops using commit.
   120  func (i *ImageService) CommitBuildStep(ctx context.Context, c backend.CommitConfig) (image.ID, error) {
   121  	ctr := i.containers.Get(c.ContainerID)
   122  	if ctr == nil {
   123  		// TODO: use typed error
   124  		return "", errors.Errorf("container not found: %s", c.ContainerID)
   125  	}
   126  	c.ContainerMountLabel = ctr.MountLabel
   127  	c.ContainerOS = ctr.OS
   128  	c.ParentImageID = string(ctr.ImageID)
   129  	return i.CommitImage(ctx, c)
   130  }