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, nil)
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, nil)
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					if found {
156						break
157					}
158				}
159				if !found {
160					continue
161				}
162			}
163			if _, ok := ref.(reference.Canonical); ok {
164				newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref))
165			}
166			if _, ok := ref.(reference.NamedTagged); ok {
167				newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref))
168			}
169		}
170		if newImage.RepoDigests == nil && newImage.RepoTags == nil {
171			if all || len(i.imageStore.Children(id)) == 0 {
172
173				if imageFilters.Contains("dangling") && !danglingOnly {
174					// dangling=false case, so dangling image is not needed
175					continue
176				}
177				if imageFilters.Contains("reference") { // skip images with no references if filtering by reference
178					continue
179				}
180				newImage.RepoDigests = []string{"<none>@<none>"}
181				newImage.RepoTags = []string{"<none>:<none>"}
182			} else {
183				continue
184			}
185		} else if danglingOnly && len(newImage.RepoTags) > 0 {
186			continue
187		}
188
189		if withExtraAttrs {
190			// lazily init variables
191			if imagesMap == nil {
192				allContainers = i.containers.List()
193
194				// allLayers is built from all layerstores combined
195				allLayers = make(map[layer.ChainID]layer.Layer)
196				for _, ls := range i.layerStores {
197					layers := ls.Map()
198					for k, v := range layers {
199						allLayers[k] = v
200					}
201				}
202				imagesMap = make(map[*image.Image]*types.ImageSummary)
203				layerRefs = make(map[layer.ChainID]int)
204			}
205
206			// Get container count
207			newImage.Containers = 0
208			for _, c := range allContainers {
209				if c.ImageID == id {
210					newImage.Containers++
211				}
212			}
213
214			// count layer references
215			rootFS := *img.RootFS
216			rootFS.DiffIDs = nil
217			for _, id := range img.RootFS.DiffIDs {
218				rootFS.Append(id)
219				chid := rootFS.ChainID()
220				layerRefs[chid]++
221				if _, ok := allLayers[chid]; !ok {
222					return nil, fmt.Errorf("layer %v was not found (corruption?)", chid)
223				}
224			}
225			imagesMap[img] = newImage
226		}
227
228		images = append(images, newImage)
229	}
230
231	if withExtraAttrs {
232		// Get Shared sizes
233		for img, newImage := range imagesMap {
234			rootFS := *img.RootFS
235			rootFS.DiffIDs = nil
236
237			newImage.SharedSize = 0
238			for _, id := range img.RootFS.DiffIDs {
239				rootFS.Append(id)
240				chid := rootFS.ChainID()
241
242				diffSize, err := allLayers[chid].DiffSize()
243				if err != nil {
244					return nil, err
245				}
246
247				if layerRefs[chid] > 1 {
248					newImage.SharedSize += diffSize
249				}
250			}
251		}
252	}
253
254	sort.Sort(sort.Reverse(byCreated(images)))
255
256	return images, nil
257}
258
259// SquashImage creates a new image with the diff of the specified image and the specified parent.
260// This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between.
261// The existing image(s) is not destroyed.
262// 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.
263func (i *ImageService) SquashImage(id, parent string) (string, error) {
264
265	var (
266		img *image.Image
267		err error
268	)
269	if img, err = i.imageStore.Get(image.ID(id)); err != nil {
270		return "", err
271	}
272
273	var parentImg *image.Image
274	var parentChainID layer.ChainID
275	if len(parent) != 0 {
276		parentImg, err = i.imageStore.Get(image.ID(parent))
277		if err != nil {
278			return "", errors.Wrap(err, "error getting specified parent layer")
279		}
280		parentChainID = parentImg.RootFS.ChainID()
281	} else {
282		rootFS := image.NewRootFS()
283		parentImg = &image.Image{RootFS: rootFS}
284	}
285	if !system.IsOSSupported(img.OperatingSystem()) {
286		return "", errors.Wrap(err, system.ErrNotSupportedOperatingSystem.Error())
287	}
288	l, err := i.layerStores[img.OperatingSystem()].Get(img.RootFS.ChainID())
289	if err != nil {
290		return "", errors.Wrap(err, "error getting image layer")
291	}
292	defer i.layerStores[img.OperatingSystem()].Release(l)
293
294	ts, err := l.TarStreamFrom(parentChainID)
295	if err != nil {
296		return "", errors.Wrapf(err, "error getting tar stream to parent")
297	}
298	defer ts.Close()
299
300	newL, err := i.layerStores[img.OperatingSystem()].Register(ts, parentChainID)
301	if err != nil {
302		return "", errors.Wrap(err, "error registering layer")
303	}
304	defer i.layerStores[img.OperatingSystem()].Release(newL)
305
306	newImage := *img
307	newImage.RootFS = nil
308
309	rootFS := *parentImg.RootFS
310	rootFS.DiffIDs = append(rootFS.DiffIDs, newL.DiffID())
311	newImage.RootFS = &rootFS
312
313	for i, hi := range newImage.History {
314		if i >= len(parentImg.History) {
315			hi.EmptyLayer = true
316		}
317		newImage.History[i] = hi
318	}
319
320	now := time.Now()
321	var historyComment string
322	if len(parent) > 0 {
323		historyComment = fmt.Sprintf("merge %s to %s", id, parent)
324	} else {
325		historyComment = fmt.Sprintf("create new from %s", id)
326	}
327
328	newImage.History = append(newImage.History, image.History{
329		Created: now,
330		Comment: historyComment,
331	})
332	newImage.Created = now
333
334	b, err := json.Marshal(&newImage)
335	if err != nil {
336		return "", errors.Wrap(err, "error marshalling image config")
337	}
338
339	newImgID, err := i.imageStore.Create(b)
340	if err != nil {
341		return "", errors.Wrap(err, "error creating new image after squash")
342	}
343	return string(newImgID), nil
344}
345
346func newImage(image *image.Image, size int64) *types.ImageSummary {
347	newImage := new(types.ImageSummary)
348	newImage.ParentID = image.Parent.String()
349	newImage.ID = image.ID().String()
350	newImage.Created = image.Created.Unix()
351	newImage.Size = size
352	newImage.VirtualSize = size
353	newImage.SharedSize = -1
354	newImage.Containers = -1
355	if image.Config != nil {
356		newImage.Labels = image.Config.Labels
357	}
358	return newImage
359}
360