github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/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 newLayer, err := l.loadLayer(layerPath, rootFS) 77 if err != nil { 78 return err 79 } 80 defer layer.ReleaseAndLog(l.ls, newLayer) 81 if expected, actual := diffID, newLayer.DiffID(); expected != actual { 82 return fmt.Errorf("invalid diffID for layer %d: expected %q, got %q", i, expected, actual) 83 } 84 rootFS.Append(diffID) 85 } 86 87 imgID, err := l.is.Create(config) 88 if err != nil { 89 return err 90 } 91 92 for _, repoTag := range m.RepoTags { 93 named, err := reference.ParseNamed(repoTag) 94 if err != nil { 95 return err 96 } 97 ref, ok := named.(reference.NamedTagged) 98 if !ok { 99 return fmt.Errorf("invalid tag %q", repoTag) 100 } 101 l.setLoadedTag(ref, imgID, outStream) 102 } 103 104 } 105 106 return nil 107 } 108 109 func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS) (layer.Layer, error) { 110 rawTar, err := os.Open(filename) 111 if err != nil { 112 logrus.Debugf("Error reading embedded tar: %v", err) 113 return nil, err 114 } 115 defer rawTar.Close() 116 117 inflatedLayerData, err := archive.DecompressStream(rawTar) 118 if err != nil { 119 return nil, err 120 } 121 defer inflatedLayerData.Close() 122 123 return l.ls.Register(inflatedLayerData, rootFS.ChainID()) 124 } 125 126 func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, outStream io.Writer) error { 127 if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID { 128 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 129 } 130 131 if err := l.rs.AddTag(ref, imgID, true); err != nil { 132 return err 133 } 134 return nil 135 } 136 137 func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error { 138 legacyLoadedMap := make(map[string]image.ID) 139 140 dirs, err := ioutil.ReadDir(tmpDir) 141 if err != nil { 142 return err 143 } 144 145 // every dir represents an image 146 for _, d := range dirs { 147 if d.IsDir() { 148 if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap); err != nil { 149 return err 150 } 151 } 152 } 153 154 // load tags from repositories file 155 repositoriesPath, err := safePath(tmpDir, legacyRepositoriesFileName) 156 if err != nil { 157 return err 158 } 159 repositoriesFile, err := os.Open(repositoriesPath) 160 if err != nil { 161 if !os.IsNotExist(err) { 162 return err 163 } 164 return repositoriesFile.Close() 165 } 166 defer repositoriesFile.Close() 167 168 repositories := make(map[string]map[string]string) 169 if err := json.NewDecoder(repositoriesFile).Decode(&repositories); err != nil { 170 return err 171 } 172 173 for name, tagMap := range repositories { 174 for tag, oldID := range tagMap { 175 imgID, ok := legacyLoadedMap[oldID] 176 if !ok { 177 return fmt.Errorf("invalid target ID: %v", oldID) 178 } 179 named, err := reference.WithName(name) 180 if err != nil { 181 return err 182 } 183 ref, err := reference.WithTag(named, tag) 184 if err != nil { 185 return err 186 } 187 l.setLoadedTag(ref, imgID, outStream) 188 } 189 } 190 191 return nil 192 } 193 194 func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[string]image.ID) error { 195 if _, loaded := loadedMap[oldID]; loaded { 196 return nil 197 } 198 configPath, err := safePath(sourceDir, filepath.Join(oldID, legacyConfigFileName)) 199 if err != nil { 200 return err 201 } 202 imageJSON, err := ioutil.ReadFile(configPath) 203 if err != nil { 204 logrus.Debugf("Error reading json: %v", err) 205 return err 206 } 207 208 var img struct{ Parent string } 209 if err := json.Unmarshal(imageJSON, &img); err != nil { 210 return err 211 } 212 213 var parentID image.ID 214 if img.Parent != "" { 215 for { 216 var loaded bool 217 if parentID, loaded = loadedMap[img.Parent]; !loaded { 218 if err := l.legacyLoadImage(img.Parent, sourceDir, loadedMap); err != nil { 219 return err 220 } 221 } else { 222 break 223 } 224 } 225 } 226 227 // todo: try to connect with migrate code 228 rootFS := image.NewRootFS() 229 var history []image.History 230 231 if parentID != "" { 232 parentImg, err := l.is.Get(parentID) 233 if err != nil { 234 return err 235 } 236 237 rootFS = parentImg.RootFS 238 history = parentImg.History 239 } 240 241 layerPath, err := safePath(sourceDir, filepath.Join(oldID, legacyLayerFileName)) 242 if err != nil { 243 return err 244 } 245 newLayer, err := l.loadLayer(layerPath, *rootFS) 246 if err != nil { 247 return err 248 } 249 rootFS.Append(newLayer.DiffID()) 250 251 h, err := v1.HistoryFromConfig(imageJSON, false) 252 if err != nil { 253 return err 254 } 255 history = append(history, h) 256 257 config, err := v1.MakeConfigFromV1Config(imageJSON, rootFS, history) 258 if err != nil { 259 return err 260 } 261 imgID, err := l.is.Create(config) 262 if err != nil { 263 return err 264 } 265 266 metadata, err := l.ls.Release(newLayer) 267 layer.LogReleaseMetadata(metadata) 268 if err != nil { 269 return err 270 } 271 272 if parentID != "" { 273 if err := l.is.SetParent(imgID, parentID); err != nil { 274 return err 275 } 276 } 277 278 loadedMap[oldID] = imgID 279 return nil 280 } 281 282 func safePath(base, path string) (string, error) { 283 return symlink.FollowSymlinkInScope(filepath.Join(base, path), base) 284 }