dotcloud/docker

View on GitHub
daemon/images/image_squash.go

Summary

Maintainability
A
2 hrs
Test Coverage
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
}