github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/migrate/v1/migratev1.go (about) 1 package v1 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 10 "encoding/json" 11 12 "github.com/Sirupsen/logrus" 13 "github.com/docker/distribution/digest" 14 "github.com/docker/docker/distribution/metadata" 15 "github.com/docker/docker/image" 16 imagev1 "github.com/docker/docker/image/v1" 17 "github.com/docker/docker/layer" 18 "github.com/docker/docker/reference" 19 ) 20 21 type graphIDRegistrar interface { 22 RegisterByGraphID(string, layer.ChainID, string) (layer.Layer, error) 23 Release(layer.Layer) ([]layer.Metadata, error) 24 } 25 26 type graphIDMounter interface { 27 MountByGraphID(string, string, layer.ChainID) (layer.RWLayer, error) 28 Unmount(string) error 29 } 30 31 const ( 32 graphDirName = "graph" 33 tarDataFileName = "tar-data.json.gz" 34 migrationFileName = ".migration-v1-images.json" 35 migrationTagsFileName = ".migration-v1-tags" 36 containersDirName = "containers" 37 configFileNameLegacy = "config.json" 38 configFileName = "config.v2.json" 39 repositoriesFilePrefixLegacy = "repositories-" 40 ) 41 42 var ( 43 errUnsupported = errors.New("migration is not supported") 44 ) 45 46 // Migrate takes an old graph directory and transforms the metadata into the 47 // new format. 48 func Migrate(root, driverName string, ls layer.Store, is image.Store, rs reference.Store, ms metadata.Store) error { 49 mappings := make(map[string]image.ID) 50 51 if registrar, ok := ls.(graphIDRegistrar); !ok { 52 return errUnsupported 53 } else if err := migrateImages(root, registrar, is, ms, mappings); err != nil { 54 return err 55 } 56 57 if mounter, ok := ls.(graphIDMounter); !ok { 58 return errUnsupported 59 } else if err := migrateContainers(root, mounter, is, mappings); err != nil { 60 return err 61 } 62 63 if err := migrateRefs(root, driverName, rs, mappings); err != nil { 64 return err 65 } 66 67 return nil 68 } 69 70 func migrateImages(root string, ls graphIDRegistrar, is image.Store, ms metadata.Store, mappings map[string]image.ID) error { 71 graphDir := filepath.Join(root, graphDirName) 72 if _, err := os.Lstat(graphDir); err != nil { 73 if os.IsNotExist(err) { 74 return nil 75 } 76 return err 77 } 78 79 mfile := filepath.Join(root, migrationFileName) 80 f, err := os.Open(mfile) 81 if err != nil && !os.IsNotExist(err) { 82 return err 83 } else if err == nil { 84 err := json.NewDecoder(f).Decode(&mappings) 85 if err != nil { 86 f.Close() 87 return err 88 } 89 f.Close() 90 } 91 92 dir, err := ioutil.ReadDir(graphDir) 93 if err != nil { 94 return err 95 } 96 for _, v := range dir { 97 v1ID := v.Name() 98 if err := imagev1.ValidateID(v1ID); err != nil { 99 continue 100 } 101 if _, exists := mappings[v1ID]; exists { 102 continue 103 } 104 if err := migrateImage(v1ID, root, ls, is, ms, mappings); err != nil { 105 continue 106 } 107 } 108 109 f, err = os.OpenFile(mfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 110 if err != nil { 111 return err 112 } 113 defer f.Close() 114 if err := json.NewEncoder(f).Encode(mappings); err != nil { 115 return err 116 } 117 118 return nil 119 } 120 121 func migrateContainers(root string, ls graphIDMounter, is image.Store, imageMappings map[string]image.ID) error { 122 containersDir := filepath.Join(root, containersDirName) 123 dir, err := ioutil.ReadDir(containersDir) 124 if err != nil { 125 return err 126 } 127 for _, v := range dir { 128 id := v.Name() 129 130 if _, err := os.Stat(filepath.Join(containersDir, id, configFileName)); err == nil { 131 continue 132 } 133 134 containerJSON, err := ioutil.ReadFile(filepath.Join(containersDir, id, configFileNameLegacy)) 135 if err != nil { 136 return err 137 } 138 139 var c map[string]*json.RawMessage 140 if err := json.Unmarshal(containerJSON, &c); err != nil { 141 return err 142 } 143 144 imageStrJSON, ok := c["Image"] 145 if !ok { 146 return fmt.Errorf("invalid container configuration for %v", id) 147 } 148 149 var image string 150 if err := json.Unmarshal([]byte(*imageStrJSON), &image); err != nil { 151 return err 152 } 153 imageID, ok := imageMappings[image] 154 if !ok { 155 logrus.Errorf("image not migrated %v", imageID) // non-fatal error 156 continue 157 } 158 159 c["Image"] = rawJSON(imageID) 160 161 containerJSON, err = json.Marshal(c) 162 if err != nil { 163 return err 164 } 165 166 if err := ioutil.WriteFile(filepath.Join(containersDir, id, configFileName), containerJSON, 0600); err != nil { 167 return err 168 } 169 170 img, err := is.Get(imageID) 171 if err != nil { 172 return err 173 } 174 175 _, err = ls.MountByGraphID(id, id, img.RootFS.ChainID()) 176 if err != nil { 177 return err 178 } 179 180 err = ls.Unmount(id) 181 if err != nil { 182 return err 183 } 184 185 logrus.Infof("migrated container %s to point to %s", id, imageID) 186 187 } 188 return nil 189 } 190 191 type refAdder interface { 192 AddTag(ref reference.Named, id image.ID, force bool) error 193 AddDigest(ref reference.Canonical, id image.ID, force bool) error 194 } 195 196 func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image.ID) error { 197 migrationFile := filepath.Join(root, migrationTagsFileName) 198 if _, err := os.Lstat(migrationFile); !os.IsNotExist(err) { 199 return err 200 } 201 202 type repositories struct { 203 Repositories map[string]map[string]string 204 } 205 206 var repos repositories 207 208 f, err := os.Open(filepath.Join(root, repositoriesFilePrefixLegacy+driverName)) 209 if err != nil { 210 if os.IsNotExist(err) { 211 return nil 212 } 213 return err 214 } 215 defer f.Close() 216 if err := json.NewDecoder(f).Decode(&repos); err != nil { 217 return err 218 } 219 220 for name, repo := range repos.Repositories { 221 for tag, id := range repo { 222 if strongID, exists := mappings[id]; exists { 223 ref, err := reference.WithName(name) 224 if err != nil { 225 logrus.Errorf("migrate tags: invalid name %q, %q", name, err) 226 continue 227 } 228 if dgst, err := digest.ParseDigest(tag); err == nil { 229 canonical, err := reference.WithDigest(ref, dgst) 230 if err != nil { 231 logrus.Errorf("migrate tags: invalid digest %q, %q", dgst, err) 232 continue 233 } 234 if err := rs.AddDigest(canonical, strongID, false); err != nil { 235 logrus.Errorf("can't migrate digest %q for %q, err: %q", ref.String(), strongID, err) 236 } 237 } else { 238 tagRef, err := reference.WithTag(ref, tag) 239 if err != nil { 240 logrus.Errorf("migrate tags: invalid tag %q, %q", tag, err) 241 continue 242 } 243 if err := rs.AddTag(tagRef, strongID, false); err != nil { 244 logrus.Errorf("can't migrate tag %q for %q, err: %q", ref.String(), strongID, err) 245 } 246 } 247 logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID) 248 } 249 } 250 } 251 252 mf, err := os.Create(migrationFile) 253 if err != nil { 254 return err 255 } 256 mf.Close() 257 258 return nil 259 } 260 261 func migrateImage(id, root string, ls graphIDRegistrar, is image.Store, ms metadata.Store, mappings map[string]image.ID) (err error) { 262 defer func() { 263 if err != nil { 264 logrus.Errorf("migration failed for %v, err: %v", id, err) 265 } 266 }() 267 268 jsonFile := filepath.Join(root, graphDirName, id, "json") 269 imageJSON, err := ioutil.ReadFile(jsonFile) 270 if err != nil { 271 return err 272 } 273 var parent struct { 274 Parent string 275 ParentID digest.Digest `json:"parent_id"` 276 } 277 if err := json.Unmarshal(imageJSON, &parent); err != nil { 278 return err 279 } 280 if parent.Parent == "" && parent.ParentID != "" { // v1.9 281 parent.Parent = parent.ParentID.Hex() 282 } 283 // compatibilityID for parent 284 parentCompatibilityID, err := ioutil.ReadFile(filepath.Join(root, graphDirName, id, "parent")) 285 if err == nil && len(parentCompatibilityID) > 0 { 286 parent.Parent = string(parentCompatibilityID) 287 } 288 289 var parentID image.ID 290 if parent.Parent != "" { 291 var exists bool 292 if parentID, exists = mappings[parent.Parent]; !exists { 293 if err := migrateImage(parent.Parent, root, ls, is, ms, mappings); err != nil { 294 // todo: fail or allow broken chains? 295 return err 296 } 297 parentID = mappings[parent.Parent] 298 } 299 } 300 301 rootFS := image.NewRootFS() 302 var history []image.History 303 304 if parentID != "" { 305 parentImg, err := is.Get(parentID) 306 if err != nil { 307 return err 308 } 309 310 rootFS = parentImg.RootFS 311 history = parentImg.History 312 } 313 314 layer, err := ls.RegisterByGraphID(id, rootFS.ChainID(), filepath.Join(filepath.Join(root, graphDirName, id, tarDataFileName))) 315 if err != nil { 316 return err 317 } 318 logrus.Infof("migrated layer %s to %s", id, layer.DiffID()) 319 320 h, err := imagev1.HistoryFromConfig(imageJSON, false) 321 if err != nil { 322 return err 323 } 324 history = append(history, h) 325 326 rootFS.Append(layer.DiffID()) 327 328 config, err := imagev1.MakeConfigFromV1Config(imageJSON, rootFS, history) 329 if err != nil { 330 return err 331 } 332 strongID, err := is.Create(config) 333 if err != nil { 334 return err 335 } 336 logrus.Infof("migrated image %s to %s", id, strongID) 337 338 if parentID != "" { 339 if err := is.SetParent(strongID, parentID); err != nil { 340 return err 341 } 342 } 343 344 checksum, err := ioutil.ReadFile(filepath.Join(root, graphDirName, id, "checksum")) 345 if err == nil { // best effort 346 dgst, err := digest.ParseDigest(string(checksum)) 347 if err == nil { 348 blobSumService := metadata.NewBlobSumService(ms) 349 blobSumService.Add(layer.DiffID(), dgst) 350 } 351 } 352 _, err = ls.Release(layer) 353 if err != nil { 354 return err 355 } 356 357 mappings[id] = strongID 358 return 359 } 360 361 func rawJSON(value interface{}) *json.RawMessage { 362 jsonval, err := json.Marshal(value) 363 if err != nil { 364 return nil 365 } 366 return (*json.RawMessage)(&jsonval) 367 }