cache.go 6.5 KB

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