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