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