export.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package graph
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "time"
  10. "github.com/Sirupsen/logrus"
  11. "github.com/docker/distribution/digest"
  12. "github.com/docker/docker/pkg/archive"
  13. "github.com/docker/docker/pkg/parsers"
  14. "github.com/docker/docker/registry"
  15. )
  16. // ImageExport exports list of images to a output stream specified in the
  17. // config. The exported images are archived into a tar when written to the
  18. // output stream. All images with the given tag and all versions containing the
  19. // same tag are exported. names is the set of tags to export, and outStream
  20. // is the writer which the images are written to.
  21. func (s *TagStore) ImageExport(names []string, outStream io.Writer) error {
  22. // get image json
  23. tempdir, err := ioutil.TempDir("", "docker-export-")
  24. if err != nil {
  25. return err
  26. }
  27. defer os.RemoveAll(tempdir)
  28. rootRepoMap := map[string]repository{}
  29. addKey := func(name string, tag string, id string) {
  30. logrus.Debugf("add key [%s:%s]", name, tag)
  31. if repo, ok := rootRepoMap[name]; !ok {
  32. rootRepoMap[name] = repository{tag: id}
  33. } else {
  34. repo[tag] = id
  35. }
  36. }
  37. for _, name := range names {
  38. name = registry.NormalizeLocalName(name)
  39. logrus.Debugf("Serializing %s", name)
  40. rootRepo := s.Repositories[name]
  41. if rootRepo != nil {
  42. // this is a base repo name, like 'busybox'
  43. for tag, id := range rootRepo {
  44. addKey(name, tag, id)
  45. if err := s.exportImage(id, tempdir); err != nil {
  46. return err
  47. }
  48. }
  49. } else {
  50. img, err := s.LookupImage(name)
  51. if err != nil {
  52. return err
  53. }
  54. if img != nil {
  55. // This is a named image like 'busybox:latest'
  56. repoName, repoTag := parsers.ParseRepositoryTag(name)
  57. // Skip digests on save
  58. if _, err := digest.ParseDigest(repoTag); err == nil {
  59. repoTag = ""
  60. }
  61. // check this length, because a lookup of a truncated has will not have a tag
  62. // and will not need to be added to this map
  63. if len(repoTag) > 0 {
  64. addKey(repoName, repoTag, img.ID)
  65. }
  66. if err := s.exportImage(img.ID, tempdir); err != nil {
  67. return err
  68. }
  69. } else {
  70. // this must be an ID that didn't get looked up just right?
  71. if err := s.exportImage(name, tempdir); err != nil {
  72. return err
  73. }
  74. }
  75. }
  76. logrus.Debugf("End Serializing %s", name)
  77. }
  78. // write repositories, if there is something to write
  79. if len(rootRepoMap) > 0 {
  80. f, err := os.OpenFile(filepath.Join(tempdir, "repositories"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
  81. if err != nil {
  82. f.Close()
  83. return err
  84. }
  85. if err := json.NewEncoder(f).Encode(rootRepoMap); err != nil {
  86. return err
  87. }
  88. if err := f.Close(); err != nil {
  89. return err
  90. }
  91. if err := os.Chtimes(filepath.Join(tempdir, "repositories"), time.Unix(0, 0), time.Unix(0, 0)); err != nil {
  92. return err
  93. }
  94. } else {
  95. logrus.Debugf("There were no repositories to write")
  96. }
  97. fs, err := archive.Tar(tempdir, archive.Uncompressed)
  98. if err != nil {
  99. return err
  100. }
  101. defer fs.Close()
  102. if _, err := io.Copy(outStream, fs); err != nil {
  103. return err
  104. }
  105. logrus.Debugf("End export image")
  106. return nil
  107. }
  108. func (s *TagStore) exportImage(name, tempdir string) error {
  109. for n := name; n != ""; {
  110. img, err := s.LookupImage(n)
  111. if err != nil || img == nil {
  112. return fmt.Errorf("No such image %s", n)
  113. }
  114. // temporary directory
  115. tmpImageDir := filepath.Join(tempdir, n)
  116. if err := os.Mkdir(tmpImageDir, os.FileMode(0755)); err != nil {
  117. if os.IsExist(err) {
  118. return nil
  119. }
  120. return err
  121. }
  122. var version = "1.0"
  123. var versionBuf = []byte(version)
  124. if err := ioutil.WriteFile(filepath.Join(tmpImageDir, "VERSION"), versionBuf, os.FileMode(0644)); err != nil {
  125. return err
  126. }
  127. imageInspectRaw, err := json.Marshal(img)
  128. if err != nil {
  129. return err
  130. }
  131. // serialize json
  132. json, err := os.Create(filepath.Join(tmpImageDir, "json"))
  133. if err != nil {
  134. return err
  135. }
  136. written, err := json.Write(imageInspectRaw)
  137. if err != nil {
  138. return err
  139. }
  140. if written != len(imageInspectRaw) {
  141. logrus.Warnf("%d byes should have been written instead %d have been written", written, len(imageInspectRaw))
  142. }
  143. // serialize filesystem
  144. fsTar, err := os.Create(filepath.Join(tmpImageDir, "layer.tar"))
  145. if err != nil {
  146. return err
  147. }
  148. if err := s.imageTarLayer(n, fsTar); err != nil {
  149. return err
  150. }
  151. for _, fname := range []string{"", "VERSION", "json", "layer.tar"} {
  152. if err := os.Chtimes(filepath.Join(tmpImageDir, fname), img.Created, img.Created); err != nil {
  153. return err
  154. }
  155. }
  156. // try again with parent
  157. n = img.Parent
  158. }
  159. return nil
  160. }