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