1package images // import "github.com/docker/docker/daemon/images"
2
3import (
4	"encoding/json"
5	"fmt"
6	"sort"
7	"time"
8
9	"github.com/pkg/errors"
10
11	"github.com/docker/distribution/reference"
12	"github.com/docker/docker/api/types"
13	"github.com/docker/docker/api/types/filters"
14	"github.com/docker/docker/container"
15	"github.com/docker/docker/image"
16	"github.com/docker/docker/layer"
17	"github.com/docker/docker/pkg/system"
18)
19
20var acceptedImageFilterTags = map[string]bool{
21	"dangling":  true,
22	"label":     true,
23	"before":    true,
24	"since":     true,
25	"reference": true,
26}
27
28// byCreated is a temporary type used to sort a list of images by creation
29// time.
30type byCreated []*types.ImageSummary
31
32func (r byCreated) Len() int           { return len(r) }
33func (r byCreated) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
34func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
35
36// Map returns a map of all images in the ImageStore
37func (i *ImageService) Map() map[image.ID]*image.Image {
38	return i.imageStore.Map()
39}
40
41// Images returns a filtered list of images. filterArgs is a JSON-encoded set
42// of filter arguments which will be interpreted by api/types/filters.
43// filter is a shell glob string applied to repository names. The argument
44// named all controls whether all images in the graph are filtered, or just
45// the heads.
46func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) {
47	var (
48		allImages    map[image.ID]*image.Image
49		err          error
50		danglingOnly = false
51	)
52
53	if err := imageFilters.Validate(acceptedImageFilterTags); err != nil {
54		return nil, err
55	}
56
57	if imageFilters.Contains("dangling") {
58		if imageFilters.ExactMatch("dangling", "true") {
59			danglingOnly = true
60		} else if !imageFilters.ExactMatch("dangling", "false") {
61			return nil, invalidFilter{"dangling", imageFilters.Get("dangling")}
62		}
63	}
64	if danglingOnly {
65		allImages = i.imageStore.Heads()
66	} else {
67		allImages = i.imageStore.Map()
68	}
69
70	var beforeFilter, sinceFilter *image.Image
71	err = imageFilters.WalkValues("before", func(value string) error {
72		beforeFilter, err = i.GetImage(value)
73		return err
74	})
75	if err != nil {
76		return nil, err
77	}
78
79	err = imageFilters.WalkValues("since", func(value string) error {
80		sinceFilter, err = i.GetImage(value)
81		return err
82	})
83	if err != nil {
84		return nil, err
85	}
86
87	images := []*types.ImageSummary{}
88	var imagesMap map[*image.Image]*types.ImageSummary
89	var layerRefs map[layer.ChainID]int
90	var allLayers map[layer.ChainID]layer.Layer
91	var allContainers []*container.Container
92
93	for id, img := range allImages {
94		if beforeFilter != nil {
95			if img.Created.Equal(beforeFilter.Created) || img.Created.After(beforeFilter.Created) {
96				continue
97			}
98		}
99
100		if sinceFilter != nil {
101			if img.Created.Equal(sinceFilter.Created) || img.Created.Before(sinceFilter.Created) {
102				continue
103			}
104		}
105
106		if imageFilters.Contains("label") {
107			// Very old image that do not have image.Config (or even labels)
108			if img.Config == nil {
109				continue
110			}
111			// We are now sure image.Config is not nil
112			if !imageFilters.MatchKVList("label", img.Config.Labels) {
113				continue
114			}
115		}
116
117		// Skip any images with an unsupported operating system to avoid a potential
118		// panic when indexing through the layerstore. Don't error as we want to list
119		// the other images. This should never happen, but here as a safety precaution.
120		if !system.IsOSSupported(img.OperatingSystem()) {
121			continue
122		}
123
124		layerID := img.RootFS.ChainID()
125		var size int64
126		if layerID != "" {
127			l, err := i.layerStores[img.OperatingSystem()].Get(layerID)
128			if err != nil {
129				// The layer may have been deleted between the call to `Map()` or
130				// `Heads()` and the call to `Get()`, so we just ignore this error
131				if err == layer.ErrLayerDoesNotExist {
132					continue
133				}
134				return nil, err
135			}
136
137			size, err = l.Size()
138			layer.ReleaseAndLog(i.layerStores[img.OperatingSystem()], l)
139			if err != nil {
140				return nil, err
141			}
142		}
143
144		newImage := newImage(img, size)
145
146		for _, ref := range i.referenceStore.References(id.Digest()) {
147			if imageFilters.Contains("reference") {
148				var found bool
149				var matchErr error
150				for _, pattern := range imageFilters.Get("reference") {
151					found, matchErr = reference.FamiliarMatch(pattern, ref)
152					if matchErr != nil {
153						return nil, matchErr
154					}
155				}
156				if !found {
157					continue
158				}
159			}
160			if _, ok := ref.(reference.Canonical); ok {
161				newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref))
162			}
163			if _, ok := ref.(reference.NamedTagged); ok {
164				newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref))
165			}
166		}
167		if newImage.RepoDigests == nil && newImage.RepoTags == nil {
168			if all || len(i.imageStore.Children(id)) == 0 {
169
170				if imageFilters.Contains("dangling") && !danglingOnly {
171					//dangling=false case, so dangling image is not needed
172					continue
173				}
174				if imageFilters.Contains("reference") { // skip images with no references if filtering by reference
175					continue
176				}
177				newImage.RepoDigests = []string{"<none>@<none>"}
178				newImage.RepoTags = []string{"<none>:<none>"}
179			} else {
180				continue
181			}
182		} else if danglingOnly && len(newImage.RepoTags) > 0 {
183			continue
184		}
185
186		if withExtraAttrs {
187			// lazily init variables
188			if imagesMap == nil {
189				allContainers = i.containers.List()
190				allLayers = i.layerStores[img.OperatingSystem()].Map()
191				imagesMap = make(map[*image.Image]*types.ImageSummary)
192				layerRefs = make(map[layer.ChainID]int)
193			}
194
195			// Get container count
196			newImage.Containers = 0
197			for _, c := range allContainers {
198				if c.ImageID == id {
199					newImage.Containers++
200				}
201			}
202
203			// count layer references
204			rootFS := *img.RootFS
205			rootFS.DiffIDs = nil
206			for _, id := range img.RootFS.DiffIDs {
207				rootFS.Append(id)
208				chid := rootFS.ChainID()
209				layerRefs[chid]++
210				if _, ok := allLayers[chid]; !ok {
211					return nil, fmt.Errorf("layer %v was not found (corruption?)", chid)
212				}
213			}
214			imagesMap[img] = newImage
215		}
216
217		images = append(images, newImage)
218	}
219
220	if withExtraAttrs {
221		// Get Shared sizes
222		for img, newImage := range imagesMap {
223			rootFS := *img.RootFS
224			rootFS.DiffIDs = nil
225
226			newImage.SharedSize = 0
227			for _, id := range img.RootFS.DiffIDs {
228				rootFS.Append(id)
229				chid := rootFS.ChainID()
230
231				diffSize, err := allLayers[chid].DiffSize()
232				if err != nil {
233					return nil, err
234				}
235
236				if layerRefs[chid] > 1 {
237					newImage.SharedSize += diffSize
238				}
239			}
240		}
241	}
242
243	sort.Sort(sort.Reverse(byCreated(images)))
244
245	return images, nil
246}
247
248// SquashImage creates a new image with the diff of the specified image and the specified parent.
249// This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between.
250// The existing image(s) is not destroyed.
251// 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.
252func (i *ImageService) SquashImage(id, parent string) (string, error) {
253
254	var (
255		img *image.Image
256		err error
257	)
258	if img, err = i.imageStore.Get(image.ID(id)); err != nil {
259		return "", err
260	}
261
262	var parentImg *image.Image
263	var parentChainID layer.ChainID
264	if len(parent) != 0 {
265		parentImg, err = i.imageStore.Get(image.ID(parent))
266		if err != nil {
267			return "", errors.Wrap(err, "error getting specified parent layer")
268		}
269		parentChainID = parentImg.RootFS.ChainID()
270	} else {
271		rootFS := image.NewRootFS()
272		parentImg = &image.Image{RootFS: rootFS}
273	}
274	if !system.IsOSSupported(img.OperatingSystem()) {
275		return "", errors.Wrap(err, system.ErrNotSupportedOperatingSystem.Error())
276	}
277	l, err := i.layerStores[img.OperatingSystem()].Get(img.RootFS.ChainID())
278	if err != nil {
279		return "", errors.Wrap(err, "error getting image layer")
280	}
281	defer i.layerStores[img.OperatingSystem()].Release(l)
282
283	ts, err := l.TarStreamFrom(parentChainID)
284	if err != nil {
285		return "", errors.Wrapf(err, "error getting tar stream to parent")
286	}
287	defer ts.Close()
288
289	newL, err := i.layerStores[img.OperatingSystem()].Register(ts, parentChainID)
290	if err != nil {
291		return "", errors.Wrap(err, "error registering layer")
292	}
293	defer i.layerStores[img.OperatingSystem()].Release(newL)
294
295	newImage := *img
296	newImage.RootFS = nil
297
298	rootFS := *parentImg.RootFS
299	rootFS.DiffIDs = append(rootFS.DiffIDs, newL.DiffID())
300	newImage.RootFS = &rootFS
301
302	for i, hi := range newImage.History {
303		if i >= len(parentImg.History) {
304			hi.EmptyLayer = true
305		}
306		newImage.History[i] = hi
307	}
308
309	now := time.Now()
310	var historyComment string
311	if len(parent) > 0 {
312		historyComment = fmt.Sprintf("merge %s to %s", id, parent)
313	} else {
314		historyComment = fmt.Sprintf("create new from %s", id)
315	}
316
317	newImage.History = append(newImage.History, image.History{
318		Created: now,
319		Comment: historyComment,
320	})
321	newImage.Created = now
322
323	b, err := json.Marshal(&newImage)
324	if err != nil {
325		return "", errors.Wrap(err, "error marshalling image config")
326	}
327
328	newImgID, err := i.imageStore.Create(b)
329	if err != nil {
330		return "", errors.Wrap(err, "error creating new image after squash")
331	}
332	return string(newImgID), nil
333}
334
335func newImage(image *image.Image, size int64) *types.ImageSummary {
336	newImage := new(types.ImageSummary)
337	newImage.ParentID = image.Parent.String()
338	newImage.ID = image.ID().String()
339	newImage.Created = image.Created.Unix()
340	newImage.Size = size
341	newImage.VirtualSize = size
342	newImage.SharedSize = -1
343	newImage.Containers = -1
344	if image.Config != nil {
345		newImage.Labels = image.Config.Labels
346	}
347	return newImage
348}
349