github.com/kobeld/docker@v1.12.0-rc1/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 "reflect" 11 12 "github.com/Sirupsen/logrus" 13 "github.com/docker/distribution" 14 "github.com/docker/docker/image" 15 "github.com/docker/docker/image/v1" 16 "github.com/docker/docker/layer" 17 "github.com/docker/docker/pkg/archive" 18 "github.com/docker/docker/pkg/chrootarchive" 19 "github.com/docker/docker/pkg/progress" 20 "github.com/docker/docker/pkg/streamformatter" 21 "github.com/docker/docker/pkg/stringid" 22 "github.com/docker/docker/pkg/symlink" 23 "github.com/docker/docker/reference" 24 ) 25 26 func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) error { 27 var ( 28 sf = streamformatter.NewJSONStreamFormatter() 29 progressOutput progress.Output 30 ) 31 if !quiet { 32 progressOutput = sf.NewProgressOutput(outStream, false) 33 outStream = &streamformatter.StdoutFormatter{Writer: outStream, StreamFormatter: streamformatter.NewJSONStreamFormatter()} 34 } 35 36 tmpDir, err := ioutil.TempDir("", "docker-import-") 37 if err != nil { 38 return err 39 } 40 defer os.RemoveAll(tmpDir) 41 42 if err := chrootarchive.Untar(inTar, tmpDir, nil); err != nil { 43 return err 44 } 45 // read manifest, if no file then load in legacy mode 46 manifestPath, err := safePath(tmpDir, manifestFileName) 47 if err != nil { 48 return err 49 } 50 manifestFile, err := os.Open(manifestPath) 51 if err != nil { 52 if os.IsNotExist(err) { 53 return l.legacyLoad(tmpDir, outStream, progressOutput) 54 } 55 return manifestFile.Close() 56 } 57 defer manifestFile.Close() 58 59 var manifest []manifestItem 60 if err := json.NewDecoder(manifestFile).Decode(&manifest); err != nil { 61 return err 62 } 63 64 var parentLinks []parentLink 65 var imageIDsStr string 66 var imageRefCount int 67 68 for _, m := range manifest { 69 configPath, err := safePath(tmpDir, m.Config) 70 if err != nil { 71 return err 72 } 73 config, err := ioutil.ReadFile(configPath) 74 if err != nil { 75 return err 76 } 77 img, err := image.NewFromJSON(config) 78 if err != nil { 79 return err 80 } 81 var rootFS image.RootFS 82 rootFS = *img.RootFS 83 rootFS.DiffIDs = nil 84 85 if expected, actual := len(m.Layers), len(img.RootFS.DiffIDs); expected != actual { 86 return fmt.Errorf("invalid manifest, layers length mismatch: expected %q, got %q", expected, actual) 87 } 88 89 for i, diffID := range img.RootFS.DiffIDs { 90 layerPath, err := safePath(tmpDir, m.Layers[i]) 91 if err != nil { 92 return err 93 } 94 r := rootFS 95 r.Append(diffID) 96 newLayer, err := l.ls.Get(r.ChainID()) 97 if err != nil { 98 newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), m.LayerSources[diffID], progressOutput) 99 if err != nil { 100 return err 101 } 102 } 103 defer layer.ReleaseAndLog(l.ls, newLayer) 104 if expected, actual := diffID, newLayer.DiffID(); expected != actual { 105 return fmt.Errorf("invalid diffID for layer %d: expected %q, got %q", i, expected, actual) 106 } 107 rootFS.Append(diffID) 108 } 109 110 imgID, err := l.is.Create(config) 111 if err != nil { 112 return err 113 } 114 imageIDsStr += fmt.Sprintf("Loaded image ID: %s\n", imgID) 115 116 imageRefCount = 0 117 for _, repoTag := range m.RepoTags { 118 named, err := reference.ParseNamed(repoTag) 119 if err != nil { 120 return err 121 } 122 ref, ok := named.(reference.NamedTagged) 123 if !ok { 124 return fmt.Errorf("invalid tag %q", repoTag) 125 } 126 l.setLoadedTag(ref, imgID, outStream) 127 outStream.Write([]byte(fmt.Sprintf("Loaded image: %s\n", ref))) 128 imageRefCount++ 129 } 130 131 parentLinks = append(parentLinks, parentLink{imgID, m.Parent}) 132 l.loggerImgEvent.LogImageEvent(imgID.String(), imgID.String(), "load") 133 } 134 135 for _, p := range validatedParentLinks(parentLinks) { 136 if p.parentID != "" { 137 if err := l.setParentID(p.id, p.parentID); err != nil { 138 return err 139 } 140 } 141 } 142 143 if imageRefCount == 0 { 144 outStream.Write([]byte(imageIDsStr)) 145 } 146 147 return nil 148 } 149 150 func (l *tarexporter) setParentID(id, parentID image.ID) error { 151 img, err := l.is.Get(id) 152 if err != nil { 153 return err 154 } 155 parent, err := l.is.Get(parentID) 156 if err != nil { 157 return err 158 } 159 if !checkValidParent(img, parent) { 160 return fmt.Errorf("image %v is not a valid parent for %v", parent.ID(), img.ID()) 161 } 162 return l.is.SetParent(id, parentID) 163 } 164 165 func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) { 166 rawTar, err := os.Open(filename) 167 if err != nil { 168 logrus.Debugf("Error reading embedded tar: %v", err) 169 return nil, err 170 } 171 defer rawTar.Close() 172 173 inflatedLayerData, err := archive.DecompressStream(rawTar) 174 if err != nil { 175 return nil, err 176 } 177 defer inflatedLayerData.Close() 178 179 if progressOutput != nil { 180 fileInfo, err := os.Stat(filename) 181 if err != nil { 182 logrus.Debugf("Error statting file: %v", err) 183 return nil, err 184 } 185 186 progressReader := progress.NewProgressReader(inflatedLayerData, progressOutput, fileInfo.Size(), stringid.TruncateID(id), "Loading layer") 187 188 if ds, ok := l.ls.(layer.DescribableStore); ok { 189 return ds.RegisterWithDescriptor(progressReader, rootFS.ChainID(), foreignSrc) 190 } 191 return l.ls.Register(progressReader, rootFS.ChainID()) 192 193 } 194 195 if ds, ok := l.ls.(layer.DescribableStore); ok { 196 return ds.RegisterWithDescriptor(inflatedLayerData, rootFS.ChainID(), foreignSrc) 197 } 198 return l.ls.Register(inflatedLayerData, rootFS.ChainID()) 199 } 200 201 func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, outStream io.Writer) error { 202 if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID { 203 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 204 } 205 206 if err := l.rs.AddTag(ref, imgID, true); err != nil { 207 return err 208 } 209 return nil 210 } 211 212 func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOutput progress.Output) error { 213 legacyLoadedMap := make(map[string]image.ID) 214 215 dirs, err := ioutil.ReadDir(tmpDir) 216 if err != nil { 217 return err 218 } 219 220 // every dir represents an image 221 for _, d := range dirs { 222 if d.IsDir() { 223 if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap, progressOutput); err != nil { 224 return err 225 } 226 } 227 } 228 229 // load tags from repositories file 230 repositoriesPath, err := safePath(tmpDir, legacyRepositoriesFileName) 231 if err != nil { 232 return err 233 } 234 repositoriesFile, err := os.Open(repositoriesPath) 235 if err != nil { 236 if !os.IsNotExist(err) { 237 return err 238 } 239 return repositoriesFile.Close() 240 } 241 defer repositoriesFile.Close() 242 243 repositories := make(map[string]map[string]string) 244 if err := json.NewDecoder(repositoriesFile).Decode(&repositories); err != nil { 245 return err 246 } 247 248 for name, tagMap := range repositories { 249 for tag, oldID := range tagMap { 250 imgID, ok := legacyLoadedMap[oldID] 251 if !ok { 252 return fmt.Errorf("invalid target ID: %v", oldID) 253 } 254 named, err := reference.WithName(name) 255 if err != nil { 256 return err 257 } 258 ref, err := reference.WithTag(named, tag) 259 if err != nil { 260 return err 261 } 262 l.setLoadedTag(ref, imgID, outStream) 263 } 264 } 265 266 return nil 267 } 268 269 func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[string]image.ID, progressOutput progress.Output) error { 270 if _, loaded := loadedMap[oldID]; loaded { 271 return nil 272 } 273 configPath, err := safePath(sourceDir, filepath.Join(oldID, legacyConfigFileName)) 274 if err != nil { 275 return err 276 } 277 imageJSON, err := ioutil.ReadFile(configPath) 278 if err != nil { 279 logrus.Debugf("Error reading json: %v", err) 280 return err 281 } 282 283 var img struct{ Parent string } 284 if err := json.Unmarshal(imageJSON, &img); err != nil { 285 return err 286 } 287 288 var parentID image.ID 289 if img.Parent != "" { 290 for { 291 var loaded bool 292 if parentID, loaded = loadedMap[img.Parent]; !loaded { 293 if err := l.legacyLoadImage(img.Parent, sourceDir, loadedMap, progressOutput); err != nil { 294 return err 295 } 296 } else { 297 break 298 } 299 } 300 } 301 302 // todo: try to connect with migrate code 303 rootFS := image.NewRootFS() 304 var history []image.History 305 306 if parentID != "" { 307 parentImg, err := l.is.Get(parentID) 308 if err != nil { 309 return err 310 } 311 312 rootFS = parentImg.RootFS 313 history = parentImg.History 314 } 315 316 layerPath, err := safePath(sourceDir, filepath.Join(oldID, legacyLayerFileName)) 317 if err != nil { 318 return err 319 } 320 newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, distribution.Descriptor{}, progressOutput) 321 if err != nil { 322 return err 323 } 324 rootFS.Append(newLayer.DiffID()) 325 326 h, err := v1.HistoryFromConfig(imageJSON, false) 327 if err != nil { 328 return err 329 } 330 history = append(history, h) 331 332 config, err := v1.MakeConfigFromV1Config(imageJSON, rootFS, history) 333 if err != nil { 334 return err 335 } 336 imgID, err := l.is.Create(config) 337 if err != nil { 338 return err 339 } 340 341 metadata, err := l.ls.Release(newLayer) 342 layer.LogReleaseMetadata(metadata) 343 if err != nil { 344 return err 345 } 346 347 if parentID != "" { 348 if err := l.is.SetParent(imgID, parentID); err != nil { 349 return err 350 } 351 } 352 353 loadedMap[oldID] = imgID 354 return nil 355 } 356 357 func safePath(base, path string) (string, error) { 358 return symlink.FollowSymlinkInScope(filepath.Join(base, path), base) 359 } 360 361 type parentLink struct { 362 id, parentID image.ID 363 } 364 365 func validatedParentLinks(pl []parentLink) (ret []parentLink) { 366 mainloop: 367 for i, p := range pl { 368 ret = append(ret, p) 369 for _, p2 := range pl { 370 if p2.id == p.parentID && p2.id != p.id { 371 continue mainloop 372 } 373 } 374 ret[i].parentID = "" 375 } 376 return 377 } 378 379 func checkValidParent(img, parent *image.Image) bool { 380 if len(img.History) == 0 && len(parent.History) == 0 { 381 return true // having history is not mandatory 382 } 383 if len(img.History)-len(parent.History) != 1 { 384 return false 385 } 386 for i, h := range parent.History { 387 if !reflect.DeepEqual(h, img.History[i]) { 388 return false 389 } 390 } 391 return true 392 }