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