daemon/images.go
4352da78
 package daemon
 
 import (
362369b4
 	"encoding/json"
4352da78
 	"fmt"
 	"sort"
362369b4
 	"time"
 
 	"github.com/pkg/errors"
4352da78
 
820b809e
 	"github.com/docker/distribution/reference"
91e197d6
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
b717de51
 	"github.com/docker/docker/container"
4352da78
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 )
 
93d1dd80
 var acceptedImageFilterTags = map[string]bool{
820b809e
 	"dangling":  true,
 	"label":     true,
 	"before":    true,
 	"since":     true,
 	"reference": true,
4352da78
 }
 
 // byCreated is a temporary type used to sort a list of images by creation
 // time.
b06c3527
 type byCreated []*types.ImageSummary
4352da78
 
 func (r byCreated) Len() int           { return len(r) }
 func (r byCreated) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
 func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
 
 // Map returns a map of all images in the ImageStore
 func (daemon *Daemon) Map() map[image.ID]*image.Image {
ce8e529e
 	return daemon.imageStore.Map()
4352da78
 }
 
 // Images returns a filtered list of images. filterArgs is a JSON-encoded set
e98cae49
 // of filter arguments which will be interpreted by api/types/filters.
4352da78
 // filter is a shell glob string applied to repository names. The argument
 // named all controls whether all images in the graph are filtered, or just
 // the heads.
820b809e
 func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) {
4352da78
 	var (
 		allImages    map[image.ID]*image.Image
 		err          error
 		danglingOnly = false
 	)
 
93d1dd80
 	if err := imageFilters.Validate(acceptedImageFilterTags); err != nil {
 		return nil, err
4352da78
 	}
 
97c5ae25
 	if imageFilters.Contains("dangling") {
93d1dd80
 		if imageFilters.ExactMatch("dangling", "true") {
 			danglingOnly = true
 		} else if !imageFilters.ExactMatch("dangling", "false") {
ebcb7d6b
 			return nil, invalidFilter{"dangling", imageFilters.Get("dangling")}
4352da78
 		}
 	}
 	if danglingOnly {
ce8e529e
 		allImages = daemon.imageStore.Heads()
4352da78
 	} else {
ce8e529e
 		allImages = daemon.imageStore.Map()
4352da78
 	}
 
750e16f5
 	var beforeFilter, sinceFilter *image.Image
 	err = imageFilters.WalkValues("before", func(value string) error {
 		beforeFilter, err = daemon.GetImage(value)
 		return err
 	})
 	if err != nil {
 		return nil, err
 	}
 
 	err = imageFilters.WalkValues("since", func(value string) error {
 		sinceFilter, err = daemon.GetImage(value)
 		return err
 	})
 	if err != nil {
 		return nil, err
 	}
 
b06c3527
 	images := []*types.ImageSummary{}
 	var imagesMap map[*image.Image]*types.ImageSummary
b717de51
 	var layerRefs map[layer.ChainID]int
 	var allLayers map[layer.ChainID]layer.Layer
 	var allContainers []*container.Container
4352da78
 
 	for id, img := range allImages {
750e16f5
 		if beforeFilter != nil {
 			if img.Created.Equal(beforeFilter.Created) || img.Created.After(beforeFilter.Created) {
 				continue
 			}
 		}
 
 		if sinceFilter != nil {
 			if img.Created.Equal(sinceFilter.Created) || img.Created.Before(sinceFilter.Created) {
 				continue
 			}
 		}
 
97c5ae25
 		if imageFilters.Contains("label") {
93d1dd80
 			// Very old image that do not have image.Config (or even labels)
4352da78
 			if img.Config == nil {
 				continue
 			}
 			// We are now sure image.Config is not nil
 			if !imageFilters.MatchKVList("label", img.Config.Labels) {
 				continue
 			}
 		}
 
 		layerID := img.RootFS.ChainID()
 		var size int64
 		if layerID != "" {
ce8e529e
 			l, err := daemon.layerStore.Get(layerID)
4352da78
 			if err != nil {
2b9694c2
 				// The layer may have been deleted between the call to `Map()` or
 				// `Heads()` and the call to `Get()`, so we just ignore this error
 				if err == layer.ErrLayerDoesNotExist {
 					continue
 				}
4352da78
 				return nil, err
 			}
 
 			size, err = l.Size()
ce8e529e
 			layer.ReleaseAndLog(daemon.layerStore, l)
4352da78
 			if err != nil {
 				return nil, err
 			}
 		}
 
 		newImage := newImage(img, size)
 
7b9a8f46
 		for _, ref := range daemon.referenceStore.References(id.Digest()) {
97c5ae25
 			if imageFilters.Contains("reference") {
820b809e
 				var found bool
 				var matchErr error
 				for _, pattern := range imageFilters.Get("reference") {
3a127939
 					found, matchErr = reference.FamiliarMatch(pattern, ref)
820b809e
 					if matchErr != nil {
 						return nil, matchErr
4352da78
 					}
820b809e
 				}
 				if !found {
4352da78
 					continue
 				}
 			}
2655954c
 			if _, ok := ref.(reference.Canonical); ok {
3a127939
 				newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref))
4352da78
 			}
2655954c
 			if _, ok := ref.(reference.NamedTagged); ok {
3a127939
 				newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref))
4352da78
 			}
 		}
 		if newImage.RepoDigests == nil && newImage.RepoTags == nil {
ce8e529e
 			if all || len(daemon.imageStore.Children(id)) == 0 {
5ee69eb4
 
97c5ae25
 				if imageFilters.Contains("dangling") && !danglingOnly {
5ee69eb4
 					//dangling=false case, so dangling image is not needed
 					continue
 				}
97c5ae25
 				if imageFilters.Contains("reference") { // skip images with no references if filtering by reference
4352da78
 					continue
 				}
 				newImage.RepoDigests = []string{"<none>@<none>"}
 				newImage.RepoTags = []string{"<none>:<none>"}
 			} else {
 				continue
 			}
8906f830
 		} else if danglingOnly && len(newImage.RepoTags) > 0 {
4352da78
 			continue
 		}
 
b717de51
 		if withExtraAttrs {
e0577d5f
 			// lazily init variables
b650a7bd
 			if imagesMap == nil {
b717de51
 				allContainers = daemon.List()
ce8e529e
 				allLayers = daemon.layerStore.Map()
b06c3527
 				imagesMap = make(map[*image.Image]*types.ImageSummary)
b717de51
 				layerRefs = make(map[layer.ChainID]int)
 			}
 
 			// Get container count
 			newImage.Containers = 0
 			for _, c := range allContainers {
 				if c.ImageID == id {
 					newImage.Containers++
 				}
 			}
 
 			// count layer references
 			rootFS := *img.RootFS
 			rootFS.DiffIDs = nil
 			for _, id := range img.RootFS.DiffIDs {
 				rootFS.Append(id)
 				chid := rootFS.ChainID()
 				layerRefs[chid]++
 				if _, ok := allLayers[chid]; !ok {
 					return nil, fmt.Errorf("layer %v was not found (corruption?)", chid)
 				}
 			}
 			imagesMap[img] = newImage
 		}
 
4352da78
 		images = append(images, newImage)
 	}
 
b717de51
 	if withExtraAttrs {
dcc1b4ba
 		// Get Shared sizes
b717de51
 		for img, newImage := range imagesMap {
 			rootFS := *img.RootFS
 			rootFS.DiffIDs = nil
 
 			newImage.SharedSize = 0
 			for _, id := range img.RootFS.DiffIDs {
 				rootFS.Append(id)
 				chid := rootFS.ChainID()
 
 				diffSize, err := allLayers[chid].DiffSize()
 				if err != nil {
 					return nil, err
 				}
 
 				if layerRefs[chid] > 1 {
 					newImage.SharedSize += diffSize
 				}
 			}
 		}
 	}
 
4352da78
 	sort.Sort(sort.Reverse(byCreated(images)))
 
 	return images, nil
 }
 
362369b4
 // 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 (daemon *Daemon) SquashImage(id, parent string) (string, error) {
3aa4a007
 
 	var (
 		img *image.Image
 		err error
 	)
ce8e529e
 	if img, err = daemon.imageStore.Get(image.ID(id)); err != nil {
362369b4
 		return "", err
 	}
 
 	var parentImg *image.Image
 	var parentChainID layer.ChainID
 	if len(parent) != 0 {
ce8e529e
 		parentImg, err = daemon.imageStore.Get(image.ID(parent))
362369b4
 		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}
 	}
 
ce8e529e
 	l, err := daemon.layerStore.Get(img.RootFS.ChainID())
362369b4
 	if err != nil {
 		return "", errors.Wrap(err, "error getting image layer")
 	}
ce8e529e
 	defer daemon.layerStore.Release(l)
362369b4
 
 	ts, err := l.TarStreamFrom(parentChainID)
 	if err != nil {
 		return "", errors.Wrapf(err, "error getting tar stream to parent")
 	}
 	defer ts.Close()
 
ce8e529e
 	newL, err := daemon.layerStore.Register(ts, parentChainID, img.OperatingSystem())
362369b4
 	if err != nil {
 		return "", errors.Wrap(err, "error registering layer")
 	}
ce8e529e
 	defer daemon.layerStore.Release(newL)
362369b4
 
f7f101d5
 	newImage := *img
362369b4
 	newImage.RootFS = nil
 
f7f101d5
 	rootFS := *parentImg.RootFS
362369b4
 	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")
 	}
 
ce8e529e
 	newImgID, err := daemon.imageStore.Create(b)
362369b4
 	if err != nil {
 		return "", errors.Wrap(err, "error creating new image after squash")
 	}
 	return string(newImgID), nil
 }
 
22c41151
 func newImage(image *image.Image, size int64) *types.ImageSummary {
b06c3527
 	newImage := new(types.ImageSummary)
4352da78
 	newImage.ParentID = image.Parent.String()
 	newImage.ID = image.ID().String()
 	newImage.Created = image.Created.Unix()
22c41151
 	newImage.Size = size
 	newImage.VirtualSize = size
b717de51
 	newImage.SharedSize = -1
 	newImage.Containers = -1
4352da78
 	if image.Config != nil {
 		newImage.Labels = image.Config.Labels
 	}
 	return newImage
 }