|
1 | 1 | package daemon
|
2 | 2 |
|
3 | 3 | import (
|
4 |
| - "encoding/json" |
5 |
| - "fmt" |
6 |
| - "reflect" |
7 |
| - "strings" |
8 |
| - |
9 | 4 | "github.com/Sirupsen/logrus"
|
10 |
| - containertypes "github.com/docker/docker/api/types/container" |
11 | 5 | "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" |
17 | 7 | )
|
18 | 8 |
|
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 |
| - |
60 | 9 | // MakeImageCache creates a stateful image cache.
|
61 | 10 | func (daemon *Daemon) MakeImageCache(sourceRefs []string) builder.ImageCache {
|
62 | 11 | if len(sourceRefs) == 0 {
|
63 |
| - return &localImageCache{daemon} |
| 12 | + return cache.NewLocal(daemon.imageStore) |
64 | 13 | }
|
65 | 14 |
|
66 |
| - cache := &imageCache{daemon: daemon, localImageCache: &localImageCache{daemon}} |
| 15 | + cache := cache.New(daemon.imageStore) |
67 | 16 |
|
68 | 17 | for _, ref := range sourceRefs {
|
69 | 18 | img, err := daemon.GetImage(ref)
|
70 | 19 | if err != nil {
|
71 | 20 | logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err)
|
72 | 21 | continue
|
73 | 22 | }
|
74 |
| - cache.sources = append(cache.sources, img) |
| 23 | + cache.Populate(img) |
75 | 24 | }
|
76 | 25 |
|
77 | 26 | return cache
|
78 | 27 | }
|
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