export.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package graph
  2. import (
  3. "encoding/json"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "path"
  8. log "github.com/Sirupsen/logrus"
  9. "github.com/docker/docker/engine"
  10. "github.com/docker/docker/pkg/archive"
  11. "github.com/docker/docker/pkg/parsers"
  12. "github.com/docker/docker/registry"
  13. )
  14. // CmdImageExport exports all images with the given tag. All versions
  15. // containing the same tag are exported. The resulting output is an
  16. // uncompressed tar ball.
  17. // name is the set of tags to export.
  18. // out is the writer where the images are written to.
  19. func (s *TagStore) CmdImageExport(job *engine.Job) engine.Status {
  20. if len(job.Args) < 1 {
  21. return job.Errorf("Usage: %s IMAGE [IMAGE...]\n", job.Name)
  22. }
  23. // get image json
  24. tempdir, err := ioutil.TempDir("", "docker-export-")
  25. if err != nil {
  26. return job.Error(err)
  27. }
  28. defer os.RemoveAll(tempdir)
  29. rootRepoMap := map[string]Repository{}
  30. addKey := func(name string, tag string, id string) {
  31. log.Debugf("add key [%s:%s]", name, tag)
  32. if repo, ok := rootRepoMap[name]; !ok {
  33. rootRepoMap[name] = Repository{tag: id}
  34. } else {
  35. repo[tag] = id
  36. }
  37. }
  38. for _, name := range job.Args {
  39. name = registry.NormalizeLocalName(name)
  40. log.Debugf("Serializing %s", name)
  41. rootRepo := s.Repositories[name]
  42. if rootRepo != nil {
  43. // this is a base repo name, like 'busybox'
  44. for tag, id := range rootRepo {
  45. addKey(name, tag, id)
  46. if err := s.exportImage(job.Eng, id, tempdir); err != nil {
  47. return job.Error(err)
  48. }
  49. }
  50. } else {
  51. img, err := s.LookupImage(name)
  52. if err != nil {
  53. return job.Error(err)
  54. }
  55. if img != nil {
  56. // This is a named image like 'busybox:latest'
  57. repoName, repoTag := parsers.ParseRepositoryTag(name)
  58. // check this length, because a lookup of a truncated has will not have a tag
  59. // and will not need to be added to this map
  60. if len(repoTag) > 0 {
  61. addKey(repoName, repoTag, img.ID)
  62. }
  63. if err := s.exportImage(job.Eng, img.ID, tempdir); err != nil {
  64. return job.Error(err)
  65. }
  66. } else {
  67. // this must be an ID that didn't get looked up just right?
  68. if err := s.exportImage(job.Eng, name, tempdir); err != nil {
  69. return job.Error(err)
  70. }
  71. }
  72. }
  73. log.Debugf("End Serializing %s", name)
  74. }
  75. // write repositories, if there is something to write
  76. if len(rootRepoMap) > 0 {
  77. rootRepoJson, _ := json.Marshal(rootRepoMap)
  78. if err := ioutil.WriteFile(path.Join(tempdir, "repositories"), rootRepoJson, os.FileMode(0644)); err != nil {
  79. return job.Error(err)
  80. }
  81. } else {
  82. log.Debugf("There were no repositories to write")
  83. }
  84. fs, err := archive.Tar(tempdir, archive.Uncompressed)
  85. if err != nil {
  86. return job.Error(err)
  87. }
  88. defer fs.Close()
  89. if _, err := io.Copy(job.Stdout, fs); err != nil {
  90. return job.Error(err)
  91. }
  92. log.Debugf("End export job: %s", job.Name)
  93. return engine.StatusOK
  94. }
  95. // FIXME: this should be a top-level function, not a class method
  96. func (s *TagStore) exportImage(eng *engine.Engine, name, tempdir string) error {
  97. for n := name; n != ""; {
  98. // temporary directory
  99. tmpImageDir := path.Join(tempdir, n)
  100. if err := os.Mkdir(tmpImageDir, os.FileMode(0755)); err != nil {
  101. if os.IsExist(err) {
  102. return nil
  103. }
  104. return err
  105. }
  106. var version = "1.0"
  107. var versionBuf = []byte(version)
  108. if err := ioutil.WriteFile(path.Join(tmpImageDir, "VERSION"), versionBuf, os.FileMode(0644)); err != nil {
  109. return err
  110. }
  111. // serialize json
  112. json, err := os.Create(path.Join(tmpImageDir, "json"))
  113. if err != nil {
  114. return err
  115. }
  116. job := eng.Job("image_inspect", n)
  117. job.SetenvBool("raw", true)
  118. job.Stdout.Add(json)
  119. if err := job.Run(); err != nil {
  120. return err
  121. }
  122. // serialize filesystem
  123. fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar"))
  124. if err != nil {
  125. return err
  126. }
  127. job = eng.Job("image_tarlayer", n)
  128. job.Stdout.Add(fsTar)
  129. if err := job.Run(); err != nil {
  130. return err
  131. }
  132. // find parent
  133. job = eng.Job("image_get", n)
  134. info, _ := job.Stdout.AddEnv()
  135. if err := job.Run(); err != nil {
  136. return err
  137. }
  138. n = info.Get("Parent")
  139. }
  140. return nil
  141. }