github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/images/image_squash.go (about)

     1  package images // import "github.com/docker/docker/daemon/images"
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/docker/docker/image"
     9  	"github.com/docker/docker/layer"
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  // SquashImage creates a new image with the diff of the specified image and the specified parent.
    14  // This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between.
    15  // The existing image(s) is not destroyed.
    16  // If no parent is specified, a new image with the diff of all the specified image's layers merged into a new layer that has no parents.
    17  func (i *ImageService) SquashImage(id, parent string) (string, error) {
    18  	var (
    19  		img *image.Image
    20  		err error
    21  	)
    22  	if img, err = i.imageStore.Get(image.ID(id)); err != nil {
    23  		return "", err
    24  	}
    25  
    26  	var parentImg *image.Image
    27  	var parentChainID layer.ChainID
    28  	if len(parent) != 0 {
    29  		parentImg, err = i.imageStore.Get(image.ID(parent))
    30  		if err != nil {
    31  			return "", errors.Wrap(err, "error getting specified parent layer")
    32  		}
    33  		parentChainID = parentImg.RootFS.ChainID()
    34  	} else {
    35  		rootFS := image.NewRootFS()
    36  		parentImg = &image.Image{RootFS: rootFS}
    37  	}
    38  	l, err := i.layerStore.Get(img.RootFS.ChainID())
    39  	if err != nil {
    40  		return "", errors.Wrap(err, "error getting image layer")
    41  	}
    42  	defer i.layerStore.Release(l)
    43  
    44  	ts, err := l.TarStreamFrom(parentChainID)
    45  	if err != nil {
    46  		return "", errors.Wrapf(err, "error getting tar stream to parent")
    47  	}
    48  	defer ts.Close()
    49  
    50  	newL, err := i.layerStore.Register(ts, parentChainID)
    51  	if err != nil {
    52  		return "", errors.Wrap(err, "error registering layer")
    53  	}
    54  	defer i.layerStore.Release(newL)
    55  
    56  	newImage := *img
    57  	newImage.RootFS = nil
    58  
    59  	rootFS := *parentImg.RootFS
    60  	rootFS.DiffIDs = append(rootFS.DiffIDs, newL.DiffID())
    61  	newImage.RootFS = &rootFS
    62  
    63  	for i, hi := range newImage.History {
    64  		if i >= len(parentImg.History) {
    65  			hi.EmptyLayer = true
    66  		}
    67  		newImage.History[i] = hi
    68  	}
    69  
    70  	now := time.Now()
    71  	var historyComment string
    72  	if len(parent) > 0 {
    73  		historyComment = fmt.Sprintf("merge %s to %s", id, parent)
    74  	} else {
    75  		historyComment = fmt.Sprintf("create new from %s", id)
    76  	}
    77  
    78  	newImage.History = append(newImage.History, image.History{
    79  		Created: now,
    80  		Comment: historyComment,
    81  	})
    82  	newImage.Created = now
    83  
    84  	b, err := json.Marshal(&newImage)
    85  	if err != nil {
    86  		return "", errors.Wrap(err, "error marshalling image config")
    87  	}
    88  
    89  	newImgID, err := i.imageStore.Create(b)
    90  	if err != nil {
    91  		return "", errors.Wrap(err, "error creating new image after squash")
    92  	}
    93  	return string(newImgID), nil
    94  }