1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/daemon/images/images.go
Brian Goff 7a9cb29fb9 Accept platform spec on container create
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>
2020-03-20 16:10:36 -07:00

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
}