github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/image/tarexport/load.go (about) 1 package tarexport 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 11 "github.com/Sirupsen/logrus" 12 "github.com/docker/docker/image" 13 "github.com/docker/docker/image/v1" 14 "github.com/docker/docker/layer" 15 "github.com/docker/docker/pkg/archive" 16 "github.com/docker/docker/pkg/chrootarchive" 17 "github.com/docker/docker/pkg/symlink" 18 "github.com/docker/docker/reference" 19 ) 20 21 func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error { 22 tmpDir, err := ioutil.TempDir("", "docker-import-") 23 if err != nil { 24 return err 25 } 26 defer os.RemoveAll(tmpDir) 27 28 if err := chrootarchive.Untar(inTar, tmpDir, nil); err != nil { 29 return err 30 } 31 // read manifest, if no file then load in legacy mode 32 manifestPath, err := safePath(tmpDir, manifestFileName) 33 if err != nil { 34 return err 35 } 36 manifestFile, err := os.Open(manifestPath) 37 if err != nil { 38 if os.IsNotExist(err) { 39 return l.legacyLoad(tmpDir, outStream) 40 } 41 return manifestFile.Close() 42 } 43 defer manifestFile.Close() 44 45 var manifest []manifestItem 46 if err := json.NewDecoder(manifestFile).Decode(&manifest); err != nil { 47 return err 48 } 49 50 for _, m := range manifest { 51 configPath, err := safePath(tmpDir, m.Config) 52 if err != nil { 53 return err 54 } 55 config, err := ioutil.ReadFile(configPath) 56 if err != nil { 57 return err 58 } 59 img, err := image.NewFromJSON(config) 60 if err != nil { 61 return err 62 } 63 var rootFS image.RootFS 64 rootFS = *img.RootFS 65 rootFS.DiffIDs = nil 66 67 if expected, actual := len(m.Layers), len(img.RootFS.DiffIDs); expected != actual { 68 return fmt.Errorf("invalid manifest, layers length mismatch: expected %q, got %q", expected, actual) 69 } 70 71 for i, diffID := range img.RootFS.DiffIDs { 72 layerPath, err := safePath(tmpDir, m.Layers[i]) 73 if err != nil { 74 return err 75 } 76 r := rootFS 77 r.Append(diffID) 78 newLayer, err := l.ls.Get(r.ChainID()) 79 if err != nil { 80 newLayer, err = l.loadLayer(layerPath, rootFS) 81 if err != nil { 82 return err 83 } 84 } 85 defer layer.ReleaseAndLog(l.ls, newLayer) 86 if expected, actual := diffID, newLayer.DiffID(); expected != actual { 87 return fmt.Errorf("invalid diffID for layer %d: expected %q, got %q", i, expected, actual) 88 } 89 rootFS.Append(diffID) 90 } 91 92 imgID, err := l.is.Create(config) 93 if err != nil { 94 return err 95 } 96 97 for _, repoTag := range m.RepoTags { 98 named, err := reference.ParseNamed(repoTag) 99 if err != nil { 100 return err 101 } 102 ref, ok := named.(reference.NamedTagged) 103 if !ok { 104 return fmt.Errorf("invalid tag %q", repoTag) 105 } 106 l.setLoadedTag(ref, imgID, outStream) 107 } 108 109 } 110 111 return nil 112 } 113 114 func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS) (layer.Layer, error) { 115 rawTar, err := os.Open(filename) 116 if err != nil { 117 logrus.Debugf("Error reading embedded tar: %v", err) 118 return nil, err 119 } 120 defer rawTar.Close() 121 122 inflatedLayerData, err := archive.DecompressStream(rawTar) 123 if err != nil { 124 return nil, err 125 } 126 defer inflatedLayerData.Close() 127 128 return l.ls.Register(inflatedLayerData, rootFS.ChainID()) 129 } 130 131 func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, outStream io.Writer) error { 132 if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID { 133 fmt.Fprintf(outStream, "The image %s already exists, renaming the old one with ID %s to empty string\n", ref.String(), string(prevID)) // todo: this message is wrong in case of multiple tags 134 } 135 136 if err := l.rs.AddTag(ref, imgID, true); err != nil { 137 return err 138 } 139 return nil 140 } 141 142 func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error { 143 legacyLoadedMap := make(map[string]image.ID) 144 145 dirs, err := ioutil.ReadDir(tmpDir) 146 if err != nil { 147 return err 148 } 149 150 // every dir represents an image 151 for _, d := range dirs { 152 if d.IsDir() { 153 if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap); err != nil { 154 return err 155 } 156 } 157 } 158 159 // load tags from repositories file 160 repositoriesPath, err := safePath(tmpDir, legacyRepositoriesFileName) 161 if err != nil { 162 return err 163 } 164 repositoriesFile, err := os.Open(repositoriesPath) 165 if err != nil { 166 if !os.IsNotExist(err) { 167 return err 168 } 169 return repositoriesFile.Close() 170 } 171 defer repositoriesFile.Close() 172 173 repositories := make(map[string]map[string]string) 174 if err := json.NewDecoder(repositoriesFile).Decode(&repositories); err != nil { 175 return err 176 } 177 178 for name, tagMap := range repositories { 179 for tag, oldID := range tagMap { 180 imgID, ok := legacyLoadedMap[oldID] 181 if !ok { 182 return fmt.Errorf("invalid target ID: %v", oldID) 183 } 184 named, err := reference.WithName(name) 185 if err != nil { 186 return err 187 } 188 ref, err := reference.WithTag(named, tag) 189 if err != nil { 190 return err 191 } 192 l.setLoadedTag(ref, imgID, outStream) 193 } 194 } 195 196 return nil 197 } 198 199 func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[string]image.ID) error { 200 if _, loaded := loadedMap[oldID]; loaded { 201 return nil 202 } 203 configPath, err := safePath(sourceDir, filepath.Join(oldID, legacyConfigFileName)) 204 if err != nil { 205 return err 206 } 207 imageJSON, err := ioutil.ReadFile(configPath) 208 if err != nil { 209 logrus.Debugf("Error reading json: %v", err) 210 return err 211 } 212 213 var img struct{ Parent string } 214 if err := json.Unmarshal(imageJSON, &img); err != nil { 215 return err 216 } 217 218 var parentID image.ID 219 if img.Parent != "" { 220 for { 221 var loaded bool 222 if parentID, loaded = loadedMap[img.Parent]; !loaded { 223 if err := l.legacyLoadImage(img.Parent, sourceDir, loadedMap); err != nil { 224 return err 225 } 226 } else { 227 break 228 } 229 } 230 } 231 232 // todo: try to connect with migrate code 233 rootFS := image.NewRootFS() 234 var history []image.History 235 236 if parentID != "" { 237 parentImg, err := l.is.Get(parentID) 238 if err != nil { 239 return err 240 } 241 242 rootFS = parentImg.RootFS 243 history = parentImg.History 244 } 245 246 layerPath, err := safePath(sourceDir, filepath.Join(oldID, legacyLayerFileName)) 247 if err != nil { 248 return err 249 } 250 newLayer, err := l.loadLayer(layerPath, *rootFS) 251 if err != nil { 252 return err 253 } 254 rootFS.Append(newLayer.DiffID()) 255 256 h, err := v1.HistoryFromConfig(imageJSON, false) 257 if err != nil { 258 return err 259 } 260 history = append(history, h) 261 262 config, err := v1.MakeConfigFromV1Config(imageJSON, rootFS, history) 263 if err != nil { 264 return err 265 } 266 imgID, err := l.is.Create(config) 267 if err != nil { 268 return err 269 } 270 271 metadata, err := l.ls.Release(newLayer) 272 layer.LogReleaseMetadata(metadata) 273 if err != nil { 274 return err 275 } 276 277 if parentID != "" { 278 if err := l.is.SetParent(imgID, parentID); err != nil { 279 return err 280 } 281 } 282 283 loadedMap[oldID] = imgID 284 return nil 285 } 286 287 func safePath(base, path string) (string, error) { 288 return symlink.FollowSymlinkInScope(filepath.Join(base, path), base) 289 }