| package images // import "github.com/docker/docker/daemon/images" | |
| import ( | |
| "encoding/json" | |
| "fmt" | |
| "time" | |
| "github.com/docker/docker/image" | |
| "github.com/docker/docker/layer" | |
| "github.com/pkg/errors" | |
| ) | |
| // SquashImage creates a new image with the diff of the specified image and the specified parent. | |
| // This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between. | |
| // The existing image(s) is not destroyed. | |
| // 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. | |
| func (i *ImageService) SquashImage(id, parent string) (string, error) { | |
| var ( | |
| img *image.Image | |
| err error | |
| ) | |
| if img, err = i.imageStore.Get(image.ID(id)); err != nil { | |
| return "", err | |
| } | |
| var parentImg *image.Image | |
| var parentChainID layer.ChainID | |
| if len(parent) != 0 { | |
| parentImg, err = i.imageStore.Get(image.ID(parent)) | |
| if err != nil { | |
| return "", errors.Wrap(err, "error getting specified parent layer") | |
| } | |
| parentChainID = parentImg.RootFS.ChainID() | |
| } else { | |
| rootFS := image.NewRootFS() | |
| parentImg = &image.Image{RootFS: rootFS} | |
| } | |
| l, err := i.layerStore.Get(img.RootFS.ChainID()) | |
| if err != nil { | |
| return "", errors.Wrap(err, "error getting image layer") | |
| } | |
| defer i.layerStore.Release(l) | |
| ts, err := l.TarStreamFrom(parentChainID) | |
| if err != nil { | |
| return "", errors.Wrapf(err, "error getting tar stream to parent") | |
| } | |
| defer ts.Close() | |
| newL, err := i.layerStore.Register(ts, parentChainID) | |
| if err != nil { | |
| return "", errors.Wrap(err, "error registering layer") | |
| } | |
| defer i.layerStore.Release(newL) | |
| newImage := *img | |
| newImage.RootFS = nil | |
| rootFS := *parentImg.RootFS | |
| rootFS.DiffIDs = append(rootFS.DiffIDs, newL.DiffID()) | |
| newImage.RootFS = &rootFS | |
| for i, hi := range newImage.History { | |
| if i >= len(parentImg.History) { | |
| hi.EmptyLayer = true | |
| } | |
| newImage.History[i] = hi | |
| } | |
| now := time.Now() | |
| var historyComment string | |
| if len(parent) > 0 { | |
| historyComment = fmt.Sprintf("merge %s to %s", id, parent) | |
| } else { | |
| historyComment = fmt.Sprintf("create new from %s", id) | |
| } | |
| newImage.History = append(newImage.History, image.History{ | |
| Created: now, | |
| Comment: historyComment, | |
| }) | |
| newImage.Created = now | |
| b, err := json.Marshal(&newImage) | |
| if err != nil { | |
| return "", errors.Wrap(err, "error marshalling image config") | |
| } | |
| newImgID, err := i.imageStore.Create(b) | |
| if err != nil { | |
| return "", errors.Wrap(err, "error creating new image after squash") | |
| } | |
| return string(newImgID), nil | |
| } |