github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/daemon/images/image_commit.go (about)

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