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