mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
7a9cb29fb9
This enables image lookup when creating a container to fail when the reference exists but it is for the wrong platform. This prevents trying to run an image for the wrong platform, as can be the case with, for example binfmt_misc+qemu. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
359 lines
9.6 KiB
Go
359 lines
9.6 KiB
Go
package images // import "github.com/docker/docker/daemon/images"
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/docker/docker/container"
|
|
"github.com/docker/docker/image"
|
|
"github.com/docker/docker/layer"
|
|
"github.com/docker/docker/pkg/system"
|
|
)
|
|
|
|
var acceptedImageFilterTags = map[string]bool{
|
|
"dangling": true,
|
|
"label": true,
|
|
"before": true,
|
|
"since": true,
|
|
"reference": true,
|
|
}
|
|
|
|
// byCreated is a temporary type used to sort a list of images by creation
|
|
// time.
|
|
type byCreated []*types.ImageSummary
|
|
|
|
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 (i *ImageService) Map() map[image.ID]*image.Image {
|
|
return i.imageStore.Map()
|
|
}
|
|
|
|
// Images returns a filtered list of images. filterArgs is a JSON-encoded set
|
|
// of filter arguments which will be interpreted by api/types/filters.
|
|
// 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.
|
|
func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) {
|
|
var (
|
|
allImages map[image.ID]*image.Image
|
|
err error
|
|
danglingOnly = false
|
|
)
|
|
|
|
if err := imageFilters.Validate(acceptedImageFilterTags); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if imageFilters.Contains("dangling") {
|
|
if imageFilters.ExactMatch("dangling", "true") {
|
|
danglingOnly = true
|
|
} else if !imageFilters.ExactMatch("dangling", "false") {
|
|
return nil, invalidFilter{"dangling", imageFilters.Get("dangling")}
|
|
}
|
|
}
|
|
if danglingOnly {
|
|
allImages = i.imageStore.Heads()
|
|
} else {
|
|
allImages = i.imageStore.Map()
|
|
}
|
|
|
|
var beforeFilter, sinceFilter *image.Image
|
|
err = imageFilters.WalkValues("before", func(value string) error {
|
|
beforeFilter, err = i.GetImage(value, nil)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = imageFilters.WalkValues("since", func(value string) error {
|
|
sinceFilter, err = i.GetImage(value, nil)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
images := []*types.ImageSummary{}
|
|
var imagesMap map[*image.Image]*types.ImageSummary
|
|
var layerRefs map[layer.ChainID]int
|
|
var allLayers map[layer.ChainID]layer.Layer
|
|
var allContainers []*container.Container
|
|
|
|
for id, img := range allImages {
|
|
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
|
|
}
|
|
}
|
|
|
|
if imageFilters.Contains("label") {
|
|
// Very old image that do not have image.Config (or even labels)
|
|
if img.Config == nil {
|
|
continue
|
|
}
|
|
// We are now sure image.Config is not nil
|
|
if !imageFilters.MatchKVList("label", img.Config.Labels) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Skip any images with an unsupported operating system to avoid a potential
|
|
// panic when indexing through the layerstore. Don't error as we want to list
|
|
// the other images. This should never happen, but here as a safety precaution.
|
|
if !system.IsOSSupported(img.OperatingSystem()) {
|
|
continue
|
|
}
|
|
|
|
layerID := img.RootFS.ChainID()
|
|
var size int64
|
|
if layerID != "" {
|
|
l, err := i.layerStores[img.OperatingSystem()].Get(layerID)
|
|
if err != nil {
|
|
// 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
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
size, err = l.Size()
|
|
layer.ReleaseAndLog(i.layerStores[img.OperatingSystem()], l)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
newImage := newImage(img, size)
|
|
|
|
for _, ref := range i.referenceStore.References(id.Digest()) {
|
|
if imageFilters.Contains("reference") {
|
|
var found bool
|
|
var matchErr error
|
|
for _, pattern := range imageFilters.Get("reference") {
|
|
found, matchErr = reference.FamiliarMatch(pattern, ref)
|
|
if matchErr != nil {
|
|
return nil, matchErr
|
|
}
|
|
if found {
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
continue
|
|
}
|
|
}
|
|
if _, ok := ref.(reference.Canonical); ok {
|
|
newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref))
|
|
}
|
|
if _, ok := ref.(reference.NamedTagged); ok {
|
|
newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref))
|
|
}
|
|
}
|
|
if newImage.RepoDigests == nil && newImage.RepoTags == nil {
|
|
if all || len(i.imageStore.Children(id)) == 0 {
|
|
|
|
if imageFilters.Contains("dangling") && !danglingOnly {
|
|
// dangling=false case, so dangling image is not needed
|
|
continue
|
|
}
|
|
if imageFilters.Contains("reference") { // skip images with no references if filtering by reference
|
|
continue
|
|
}
|
|
newImage.RepoDigests = []string{"<none>@<none>"}
|
|
newImage.RepoTags = []string{"<none>:<none>"}
|
|
} else {
|
|
continue
|
|
}
|
|
} else if danglingOnly && len(newImage.RepoTags) > 0 {
|
|
continue
|
|
}
|
|
|
|
if withExtraAttrs {
|
|
// lazily init variables
|
|
if imagesMap == nil {
|
|
allContainers = i.containers.List()
|
|
|
|
// allLayers is built from all layerstores combined
|
|
allLayers = make(map[layer.ChainID]layer.Layer)
|
|
for _, ls := range i.layerStores {
|
|
layers := ls.Map()
|
|
for k, v := range layers {
|
|
allLayers[k] = v
|
|
}
|
|
}
|
|
imagesMap = make(map[*image.Image]*types.ImageSummary)
|
|
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
|
|
}
|
|
|
|
images = append(images, newImage)
|
|
}
|
|
|
|
if withExtraAttrs {
|
|
// Get Shared sizes
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sort.Sort(sort.Reverse(byCreated(images)))
|
|
|
|
return images, nil
|
|
}
|
|
|
|
// 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}
|
|
}
|
|
if !system.IsOSSupported(img.OperatingSystem()) {
|
|
return "", errors.Wrap(err, system.ErrNotSupportedOperatingSystem.Error())
|
|
}
|
|
l, err := i.layerStores[img.OperatingSystem()].Get(img.RootFS.ChainID())
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error getting image layer")
|
|
}
|
|
defer i.layerStores[img.OperatingSystem()].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.layerStores[img.OperatingSystem()].Register(ts, parentChainID)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error registering layer")
|
|
}
|
|
defer i.layerStores[img.OperatingSystem()].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
|
|
}
|
|
|
|
func newImage(image *image.Image, size int64) *types.ImageSummary {
|
|
newImage := new(types.ImageSummary)
|
|
newImage.ParentID = image.Parent.String()
|
|
newImage.ID = image.ID().String()
|
|
newImage.Created = image.Created.Unix()
|
|
newImage.Size = size
|
|
newImage.VirtualSize = size
|
|
newImage.SharedSize = -1
|
|
newImage.Containers = -1
|
|
if image.Config != nil {
|
|
newImage.Labels = image.Config.Labels
|
|
}
|
|
return newImage
|
|
}
|