Skip to content

Commit be1df1e

Browse files
committedJan 3, 2017
Extract the daemon image cache to its own package
ImageCache is now independent of `Daemon` and is located in `image/cache` package. Signed-off-by: Vincent Demeester <[email protected]>
1 parent 884a1c2 commit be1df1e

File tree

4 files changed

+263
-237
lines changed

4 files changed

+263
-237
lines changed
 

‎daemon/cache.go

+4-231
Original file line numberDiff line numberDiff line change
@@ -1,254 +1,27 @@
11
package daemon
22

33
import (
4-
"encoding/json"
5-
"fmt"
6-
"reflect"
7-
"strings"
8-
94
"github.com/Sirupsen/logrus"
10-
containertypes "github.com/docker/docker/api/types/container"
115
"github.com/docker/docker/builder"
12-
"github.com/docker/docker/dockerversion"
13-
"github.com/docker/docker/image"
14-
"github.com/docker/docker/layer"
15-
"github.com/docker/docker/runconfig"
16-
"github.com/pkg/errors"
6+
"github.com/docker/docker/image/cache"
177
)
188

19-
// getLocalCachedImage returns the most recent created image that is a child
20-
// of the image with imgID, that had the same config when it was
21-
// created. nil is returned if a child cannot be found. An error is
22-
// returned if the parent image cannot be found.
23-
func (daemon *Daemon) getLocalCachedImage(imgID image.ID, config *containertypes.Config) (*image.Image, error) {
24-
// Loop on the children of the given image and check the config
25-
getMatch := func(siblings []image.ID) (*image.Image, error) {
26-
var match *image.Image
27-
for _, id := range siblings {
28-
img, err := daemon.imageStore.Get(id)
29-
if err != nil {
30-
return nil, fmt.Errorf("unable to find image %q", id)
31-
}
32-
33-
if runconfig.Compare(&img.ContainerConfig, config) {
34-
// check for the most up to date match
35-
if match == nil || match.Created.Before(img.Created) {
36-
match = img
37-
}
38-
}
39-
}
40-
return match, nil
41-
}
42-
43-
// In this case, this is `FROM scratch`, which isn't an actual image.
44-
if imgID == "" {
45-
images := daemon.imageStore.Map()
46-
var siblings []image.ID
47-
for id, img := range images {
48-
if img.Parent == imgID {
49-
siblings = append(siblings, id)
50-
}
51-
}
52-
return getMatch(siblings)
53-
}
54-
55-
// find match from child images
56-
siblings := daemon.imageStore.Children(imgID)
57-
return getMatch(siblings)
58-
}
59-
609
// MakeImageCache creates a stateful image cache.
6110
func (daemon *Daemon) MakeImageCache(sourceRefs []string) builder.ImageCache {
6211
if len(sourceRefs) == 0 {
63-
return &localImageCache{daemon}
12+
return cache.NewLocal(daemon.imageStore)
6413
}
6514

66-
cache := &imageCache{daemon: daemon, localImageCache: &localImageCache{daemon}}
15+
cache := cache.New(daemon.imageStore)
6716

6817
for _, ref := range sourceRefs {
6918
img, err := daemon.GetImage(ref)
7019
if err != nil {
7120
logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err)
7221
continue
7322
}
74-
cache.sources = append(cache.sources, img)
23+
cache.Populate(img)
7524
}
7625

7726
return cache
7827
}
79-
80-
// localImageCache is cache based on parent chain.
81-
type localImageCache struct {
82-
daemon *Daemon
83-
}
84-
85-
func (lic *localImageCache) GetCache(imgID string, config *containertypes.Config) (string, error) {
86-
return getImageIDAndError(lic.daemon.getLocalCachedImage(image.ID(imgID), config))
87-
}
88-
89-
// imageCache is cache based on history objects. Requires initial set of images.
90-
type imageCache struct {
91-
sources []*image.Image
92-
daemon *Daemon
93-
localImageCache *localImageCache
94-
}
95-
96-
func (ic *imageCache) restoreCachedImage(parent, target *image.Image, cfg *containertypes.Config) (image.ID, error) {
97-
var history []image.History
98-
rootFS := image.NewRootFS()
99-
lenHistory := 0
100-
if parent != nil {
101-
history = parent.History
102-
rootFS = parent.RootFS
103-
lenHistory = len(parent.History)
104-
}
105-
history = append(history, target.History[lenHistory])
106-
if layer := getLayerForHistoryIndex(target, lenHistory); layer != "" {
107-
rootFS.Append(layer)
108-
}
109-
110-
config, err := json.Marshal(&image.Image{
111-
V1Image: image.V1Image{
112-
DockerVersion: dockerversion.Version,
113-
Config: cfg,
114-
Architecture: target.Architecture,
115-
OS: target.OS,
116-
Author: target.Author,
117-
Created: history[len(history)-1].Created,
118-
},
119-
RootFS: rootFS,
120-
History: history,
121-
OSFeatures: target.OSFeatures,
122-
OSVersion: target.OSVersion,
123-
})
124-
if err != nil {
125-
return "", errors.Wrap(err, "failed to marshal image config")
126-
}
127-
128-
imgID, err := ic.daemon.imageStore.Create(config)
129-
if err != nil {
130-
return "", errors.Wrap(err, "failed to create cache image")
131-
}
132-
133-
if parent != nil {
134-
if err := ic.daemon.imageStore.SetParent(imgID, parent.ID()); err != nil {
135-
return "", errors.Wrapf(err, "failed to set parent for %v to %v", target.ID(), parent.ID())
136-
}
137-
}
138-
return imgID, nil
139-
}
140-
141-
func (ic *imageCache) isParent(imgID, parentID image.ID) bool {
142-
nextParent, err := ic.daemon.imageStore.GetParent(imgID)
143-
if err != nil {
144-
return false
145-
}
146-
if nextParent == parentID {
147-
return true
148-
}
149-
return ic.isParent(nextParent, parentID)
150-
}
151-
152-
func (ic *imageCache) GetCache(parentID string, cfg *containertypes.Config) (string, error) {
153-
imgID, err := ic.localImageCache.GetCache(parentID, cfg)
154-
if err != nil {
155-
return "", err
156-
}
157-
if imgID != "" {
158-
for _, s := range ic.sources {
159-
if ic.isParent(s.ID(), image.ID(imgID)) {
160-
return imgID, nil
161-
}
162-
}
163-
}
164-
165-
var parent *image.Image
166-
lenHistory := 0
167-
if parentID != "" {
168-
parent, err = ic.daemon.imageStore.Get(image.ID(parentID))
169-
if err != nil {
170-
return "", errors.Wrapf(err, "unable to find image %v", parentID)
171-
}
172-
lenHistory = len(parent.History)
173-
}
174-
175-
for _, target := range ic.sources {
176-
if !isValidParent(target, parent) || !isValidConfig(cfg, target.History[lenHistory]) {
177-
continue
178-
}
179-
180-
if len(target.History)-1 == lenHistory { // last
181-
if parent != nil {
182-
if err := ic.daemon.imageStore.SetParent(target.ID(), parent.ID()); err != nil {
183-
return "", errors.Wrapf(err, "failed to set parent for %v to %v", target.ID(), parent.ID())
184-
}
185-
}
186-
return target.ID().String(), nil
187-
}
188-
189-
imgID, err := ic.restoreCachedImage(parent, target, cfg)
190-
if err != nil {
191-
return "", errors.Wrapf(err, "failed to restore cached image from %q to %v", parentID, target.ID())
192-
}
193-
194-
ic.sources = []*image.Image{target} // avoid jumping to different target, tuned for safety atm
195-
return imgID.String(), nil
196-
}
197-
198-
return "", nil
199-
}
200-
201-
func getImageIDAndError(img *image.Image, err error) (string, error) {
202-
if img == nil || err != nil {
203-
return "", err
204-
}
205-
return img.ID().String(), nil
206-
}
207-
208-
func isValidParent(img, parent *image.Image) bool {
209-
if len(img.History) == 0 {
210-
return false
211-
}
212-
if parent == nil || len(parent.History) == 0 && len(parent.RootFS.DiffIDs) == 0 {
213-
return true
214-
}
215-
if len(parent.History) >= len(img.History) {
216-
return false
217-
}
218-
if len(parent.RootFS.DiffIDs) >= len(img.RootFS.DiffIDs) {
219-
return false
220-
}
221-
222-
for i, h := range parent.History {
223-
if !reflect.DeepEqual(h, img.History[i]) {
224-
return false
225-
}
226-
}
227-
for i, d := range parent.RootFS.DiffIDs {
228-
if d != img.RootFS.DiffIDs[i] {
229-
return false
230-
}
231-
}
232-
return true
233-
}
234-
235-
func getLayerForHistoryIndex(image *image.Image, index int) layer.DiffID {
236-
layerIndex := 0
237-
for i, h := range image.History {
238-
if i == index {
239-
if h.EmptyLayer {
240-
return ""
241-
}
242-
break
243-
}
244-
if !h.EmptyLayer {
245-
layerIndex++
246-
}
247-
}
248-
return image.RootFS.DiffIDs[layerIndex] // validate?
249-
}
250-
251-
func isValidConfig(cfg *containertypes.Config, h image.History) bool {
252-
// todo: make this format better than join that loses data
253-
return strings.Join(cfg.Cmd, " ") == h.CreatedBy
254-
}

0 commit comments

Comments
 (0)
Please sign in to comment.