cache.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package daemon
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "reflect"
  6. "strings"
  7. "github.com/Sirupsen/logrus"
  8. containertypes "github.com/docker/docker/api/types/container"
  9. "github.com/docker/docker/builder"
  10. "github.com/docker/docker/dockerversion"
  11. "github.com/docker/docker/image"
  12. "github.com/docker/docker/layer"
  13. "github.com/docker/docker/runconfig"
  14. "github.com/pkg/errors"
  15. )
  16. // getLocalCachedImage returns the most recent created image that is a child
  17. // of the image with imgID, that had the same config when it was
  18. // created. nil is returned if a child cannot be found. An error is
  19. // returned if the parent image cannot be found.
  20. func (daemon *Daemon) getLocalCachedImage(imgID image.ID, config *containertypes.Config) (*image.Image, error) {
  21. // Loop on the children of the given image and check the config
  22. getMatch := func(siblings []image.ID) (*image.Image, error) {
  23. var match *image.Image
  24. for _, id := range siblings {
  25. img, err := daemon.imageStore.Get(id)
  26. if err != nil {
  27. return nil, fmt.Errorf("unable to find image %q", id)
  28. }
  29. if runconfig.Compare(&img.ContainerConfig, config) {
  30. // check for the most up to date match
  31. if match == nil || match.Created.Before(img.Created) {
  32. match = img
  33. }
  34. }
  35. }
  36. return match, nil
  37. }
  38. // In this case, this is `FROM scratch`, which isn't an actual image.
  39. if imgID == "" {
  40. images := daemon.imageStore.Map()
  41. var siblings []image.ID
  42. for id, img := range images {
  43. if img.Parent == imgID {
  44. siblings = append(siblings, id)
  45. }
  46. }
  47. return getMatch(siblings)
  48. }
  49. // find match from child images
  50. siblings := daemon.imageStore.Children(imgID)
  51. return getMatch(siblings)
  52. }
  53. // MakeImageCache creates a stateful image cache.
  54. func (daemon *Daemon) MakeImageCache(sourceRefs []string) builder.ImageCache {
  55. if len(sourceRefs) == 0 {
  56. return &localImageCache{daemon}
  57. }
  58. cache := &imageCache{daemon: daemon, localImageCache: &localImageCache{daemon}}
  59. for _, ref := range sourceRefs {
  60. img, err := daemon.GetImage(ref)
  61. if err != nil {
  62. logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err)
  63. continue
  64. }
  65. cache.sources = append(cache.sources, img)
  66. }
  67. return cache
  68. }
  69. // localImageCache is cache based on parent chain.
  70. type localImageCache struct {
  71. daemon *Daemon
  72. }
  73. func (lic *localImageCache) GetCache(imgID string, config *containertypes.Config) (string, error) {
  74. return getImageIDAndError(lic.daemon.getLocalCachedImage(image.ID(imgID), config))
  75. }
  76. // imageCache is cache based on history objects. Requires initial set of images.
  77. type imageCache struct {
  78. sources []*image.Image
  79. daemon *Daemon
  80. localImageCache *localImageCache
  81. }
  82. func (ic *imageCache) restoreCachedImage(parent, target *image.Image, cfg *containertypes.Config) (image.ID, error) {
  83. var history []image.History
  84. rootFS := image.NewRootFS()
  85. lenHistory := 0
  86. if parent != nil {
  87. history = parent.History
  88. rootFS = parent.RootFS
  89. lenHistory = len(parent.History)
  90. }
  91. history = append(history, target.History[lenHistory])
  92. if layer := getLayerForHistoryIndex(target, lenHistory); layer != "" {
  93. rootFS.Append(layer)
  94. }
  95. config, err := json.Marshal(&image.Image{
  96. V1Image: image.V1Image{
  97. DockerVersion: dockerversion.Version,
  98. Config: cfg,
  99. Architecture: target.Architecture,
  100. OS: target.OS,
  101. Author: target.Author,
  102. Created: history[len(history)-1].Created,
  103. },
  104. RootFS: rootFS,
  105. History: history,
  106. OSFeatures: target.OSFeatures,
  107. OSVersion: target.OSVersion,
  108. })
  109. if err != nil {
  110. return "", errors.Wrap(err, "failed to marshal image config")
  111. }
  112. imgID, err := ic.daemon.imageStore.Create(config)
  113. if err != nil {
  114. return "", errors.Wrap(err, "failed to create cache image")
  115. }
  116. if parent != nil {
  117. if err := ic.daemon.imageStore.SetParent(imgID, parent.ID()); err != nil {
  118. return "", errors.Wrapf(err, "failed to set parent for %v to %v", target.ID(), parent.ID())
  119. }
  120. }
  121. return imgID, nil
  122. }
  123. func (ic *imageCache) isParent(imgID, parentID image.ID) bool {
  124. nextParent, err := ic.daemon.imageStore.GetParent(imgID)
  125. if err != nil {
  126. return false
  127. }
  128. if nextParent == parentID {
  129. return true
  130. }
  131. return ic.isParent(nextParent, parentID)
  132. }
  133. func (ic *imageCache) GetCache(parentID string, cfg *containertypes.Config) (string, error) {
  134. imgID, err := ic.localImageCache.GetCache(parentID, cfg)
  135. if err != nil {
  136. return "", err
  137. }
  138. if imgID != "" {
  139. for _, s := range ic.sources {
  140. if ic.isParent(s.ID(), image.ID(imgID)) {
  141. return imgID, nil
  142. }
  143. }
  144. }
  145. var parent *image.Image
  146. lenHistory := 0
  147. if parentID != "" {
  148. parent, err = ic.daemon.imageStore.Get(image.ID(parentID))
  149. if err != nil {
  150. return "", errors.Wrapf(err, "unable to find image %v", parentID)
  151. }
  152. lenHistory = len(parent.History)
  153. }
  154. for _, target := range ic.sources {
  155. if !isValidParent(target, parent) || !isValidConfig(cfg, target.History[lenHistory]) {
  156. continue
  157. }
  158. if len(target.History)-1 == lenHistory { // last
  159. if parent != nil {
  160. if err := ic.daemon.imageStore.SetParent(target.ID(), parent.ID()); err != nil {
  161. return "", errors.Wrapf(err, "failed to set parent for %v to %v", target.ID(), parent.ID())
  162. }
  163. }
  164. return target.ID().String(), nil
  165. }
  166. imgID, err := ic.restoreCachedImage(parent, target, cfg)
  167. if err != nil {
  168. return "", errors.Wrapf(err, "failed to restore cached image from %q to %v", parentID, target.ID())
  169. }
  170. ic.sources = []*image.Image{target} // avoid jumping to different target, tuned for safety atm
  171. return imgID.String(), nil
  172. }
  173. return "", nil
  174. }
  175. func getImageIDAndError(img *image.Image, err error) (string, error) {
  176. if img == nil || err != nil {
  177. return "", err
  178. }
  179. return img.ID().String(), nil
  180. }
  181. func isValidParent(img, parent *image.Image) bool {
  182. if len(img.History) == 0 {
  183. return false
  184. }
  185. if parent == nil || len(parent.History) == 0 && len(parent.RootFS.DiffIDs) == 0 {
  186. return true
  187. }
  188. if len(parent.History) >= len(img.History) {
  189. return false
  190. }
  191. if len(parent.RootFS.DiffIDs) >= len(img.RootFS.DiffIDs) {
  192. return false
  193. }
  194. for i, h := range parent.History {
  195. if !reflect.DeepEqual(h, img.History[i]) {
  196. return false
  197. }
  198. }
  199. for i, d := range parent.RootFS.DiffIDs {
  200. if d != img.RootFS.DiffIDs[i] {
  201. return false
  202. }
  203. }
  204. return true
  205. }
  206. func getLayerForHistoryIndex(image *image.Image, index int) layer.DiffID {
  207. layerIndex := 0
  208. for i, h := range image.History {
  209. if i == index {
  210. if h.EmptyLayer {
  211. return ""
  212. }
  213. break
  214. }
  215. if !h.EmptyLayer {
  216. layerIndex++
  217. }
  218. }
  219. return image.RootFS.DiffIDs[layerIndex] // validate?
  220. }
  221. func isValidConfig(cfg *containertypes.Config, h image.History) bool {
  222. // todo: make this format better than join that loses data
  223. return strings.Join(cfg.Cmd, " ") == h.CreatedBy
  224. }