image_squash.go 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package images // import "github.com/docker/docker/daemon/images"
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "time"
  6. "github.com/docker/docker/image"
  7. "github.com/docker/docker/layer"
  8. "github.com/pkg/errors"
  9. )
  10. // SquashImage creates a new image with the diff of the specified image and the specified parent.
  11. // This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between.
  12. // The existing image(s) is not destroyed.
  13. // 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.
  14. func (i *ImageService) SquashImage(id, parent string) (string, error) {
  15. var (
  16. img *image.Image
  17. err error
  18. )
  19. if img, err = i.imageStore.Get(image.ID(id)); err != nil {
  20. return "", err
  21. }
  22. var parentImg *image.Image
  23. var parentChainID layer.ChainID
  24. if len(parent) != 0 {
  25. parentImg, err = i.imageStore.Get(image.ID(parent))
  26. if err != nil {
  27. return "", errors.Wrap(err, "error getting specified parent layer")
  28. }
  29. parentChainID = parentImg.RootFS.ChainID()
  30. } else {
  31. rootFS := image.NewRootFS()
  32. parentImg = &image.Image{RootFS: rootFS}
  33. }
  34. l, err := i.layerStore.Get(img.RootFS.ChainID())
  35. if err != nil {
  36. return "", errors.Wrap(err, "error getting image layer")
  37. }
  38. defer i.layerStore.Release(l)
  39. ts, err := l.TarStreamFrom(parentChainID)
  40. if err != nil {
  41. return "", errors.Wrapf(err, "error getting tar stream to parent")
  42. }
  43. defer ts.Close()
  44. newL, err := i.layerStore.Register(ts, parentChainID)
  45. if err != nil {
  46. return "", errors.Wrap(err, "error registering layer")
  47. }
  48. defer i.layerStore.Release(newL)
  49. newImage := *img
  50. newImage.RootFS = nil
  51. rootFS := *parentImg.RootFS
  52. rootFS.DiffIDs = append(rootFS.DiffIDs, newL.DiffID())
  53. newImage.RootFS = &rootFS
  54. for i, hi := range newImage.History {
  55. if i >= len(parentImg.History) {
  56. hi.EmptyLayer = true
  57. }
  58. newImage.History[i] = hi
  59. }
  60. now := time.Now()
  61. var historyComment string
  62. if len(parent) > 0 {
  63. historyComment = fmt.Sprintf("merge %s to %s", id, parent)
  64. } else {
  65. historyComment = fmt.Sprintf("create new from %s", id)
  66. }
  67. newImage.History = append(newImage.History, image.History{
  68. Created: &now,
  69. Comment: historyComment,
  70. })
  71. newImage.Created = &now
  72. b, err := json.Marshal(&newImage)
  73. if err != nil {
  74. return "", errors.Wrap(err, "error marshalling image config")
  75. }
  76. newImgID, err := i.imageStore.Create(b)
  77. if err != nil {
  78. return "", errors.Wrap(err, "error creating new image after squash")
  79. }
  80. return string(newImgID), nil
  81. }