github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/migrate/v1/migratev1.go (about) 1 package v1 // import "github.com/docker/docker/migrate/v1" 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "runtime" 10 "strconv" 11 "sync" 12 "time" 13 14 "encoding/json" 15 16 "github.com/docker/distribution/reference" 17 "github.com/docker/docker/distribution/metadata" 18 "github.com/docker/docker/image" 19 imagev1 "github.com/docker/docker/image/v1" 20 "github.com/docker/docker/layer" 21 "github.com/docker/docker/pkg/ioutils" 22 refstore "github.com/docker/docker/reference" 23 "github.com/opencontainers/go-digest" 24 "github.com/sirupsen/logrus" 25 ) 26 27 type graphIDRegistrar interface { 28 RegisterByGraphID(string, layer.ChainID, layer.DiffID, string, int64) (layer.Layer, error) 29 Release(layer.Layer) ([]layer.Metadata, error) 30 } 31 32 type graphIDMounter interface { 33 CreateRWLayerByGraphID(string, string, layer.ChainID) error 34 } 35 36 type checksumCalculator interface { 37 ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID layer.DiffID, size int64, err error) 38 } 39 40 const ( 41 graphDirName = "graph" 42 tarDataFileName = "tar-data.json.gz" 43 migrationFileName = ".migration-v1-images.json" 44 migrationTagsFileName = ".migration-v1-tags" 45 migrationDiffIDFileName = ".migration-diffid" 46 migrationSizeFileName = ".migration-size" 47 migrationTarDataFileName = ".migration-tardata" 48 containersDirName = "containers" 49 configFileNameLegacy = "config.json" 50 configFileName = "config.v2.json" 51 repositoriesFilePrefixLegacy = "repositories-" 52 ) 53 54 var ( 55 errUnsupported = errors.New("migration is not supported") 56 ) 57 58 // Migrate takes an old graph directory and transforms the metadata into the 59 // new format. 60 func Migrate(root, driverName string, ls layer.Store, is image.Store, rs refstore.Store, ms metadata.Store) error { 61 graphDir := filepath.Join(root, graphDirName) 62 if _, err := os.Lstat(graphDir); os.IsNotExist(err) { 63 return nil 64 } 65 66 mappings, err := restoreMappings(root) 67 if err != nil { 68 return err 69 } 70 71 if cc, ok := ls.(checksumCalculator); ok { 72 CalculateLayerChecksums(root, cc, mappings) 73 } 74 75 if registrar, ok := ls.(graphIDRegistrar); !ok { 76 return errUnsupported 77 } else if err := migrateImages(root, registrar, is, ms, mappings); err != nil { 78 return err 79 } 80 81 err = saveMappings(root, mappings) 82 if err != nil { 83 return err 84 } 85 86 if mounter, ok := ls.(graphIDMounter); !ok { 87 return errUnsupported 88 } else if err := migrateContainers(root, mounter, is, mappings); err != nil { 89 return err 90 } 91 92 return migrateRefs(root, driverName, rs, mappings) 93 } 94 95 // CalculateLayerChecksums walks an old graph directory and calculates checksums 96 // for each layer. These checksums are later used for migration. 97 func CalculateLayerChecksums(root string, ls checksumCalculator, mappings map[string]image.ID) { 98 graphDir := filepath.Join(root, graphDirName) 99 // spawn some extra workers also for maximum performance because the process is bounded by both cpu and io 100 workers := runtime.NumCPU() * 3 101 workQueue := make(chan string, workers) 102 103 wg := sync.WaitGroup{} 104 105 for i := 0; i < workers; i++ { 106 wg.Add(1) 107 go func() { 108 for id := range workQueue { 109 start := time.Now() 110 if err := calculateLayerChecksum(graphDir, id, ls); err != nil { 111 logrus.Errorf("could not calculate checksum for %q, %q", id, err) 112 } 113 elapsed := time.Since(start) 114 logrus.Debugf("layer %s took %.2f seconds", id, elapsed.Seconds()) 115 } 116 wg.Done() 117 }() 118 } 119 120 dir, err := ioutil.ReadDir(graphDir) 121 if err != nil { 122 logrus.Errorf("could not read directory %q", graphDir) 123 return 124 } 125 for _, v := range dir { 126 v1ID := v.Name() 127 if err := imagev1.ValidateID(v1ID); err != nil { 128 continue 129 } 130 if _, ok := mappings[v1ID]; ok { // support old migrations without helper files 131 continue 132 } 133 workQueue <- v1ID 134 } 135 close(workQueue) 136 wg.Wait() 137 } 138 139 func calculateLayerChecksum(graphDir, id string, ls checksumCalculator) error { 140 diffIDFile := filepath.Join(graphDir, id, migrationDiffIDFileName) 141 if _, err := os.Lstat(diffIDFile); err == nil { 142 return nil 143 } else if !os.IsNotExist(err) { 144 return err 145 } 146 147 parent, err := getParent(filepath.Join(graphDir, id)) 148 if err != nil { 149 return err 150 } 151 152 diffID, size, err := ls.ChecksumForGraphID(id, parent, filepath.Join(graphDir, id, tarDataFileName), filepath.Join(graphDir, id, migrationTarDataFileName)) 153 if err != nil { 154 return err 155 } 156 157 if err := ioutil.WriteFile(filepath.Join(graphDir, id, migrationSizeFileName), []byte(strconv.Itoa(int(size))), 0600); err != nil { 158 return err 159 } 160 161 if err := ioutils.AtomicWriteFile(filepath.Join(graphDir, id, migrationDiffIDFileName), []byte(diffID), 0600); err != nil { 162 return err 163 } 164 165 logrus.Infof("calculated checksum for layer %s: %s", id, diffID) 166 return nil 167 } 168 169 func restoreMappings(root string) (map[string]image.ID, error) { 170 mappings := make(map[string]image.ID) 171 172 mfile := filepath.Join(root, migrationFileName) 173 f, err := os.Open(mfile) 174 if err != nil && !os.IsNotExist(err) { 175 return nil, err 176 } else if err == nil { 177 err := json.NewDecoder(f).Decode(&mappings) 178 if err != nil { 179 f.Close() 180 return nil, err 181 } 182 f.Close() 183 } 184 185 return mappings, nil 186 } 187 188 func saveMappings(root string, mappings map[string]image.ID) error { 189 mfile := filepath.Join(root, migrationFileName) 190 f, err := os.OpenFile(mfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 191 if err != nil { 192 return err 193 } 194 defer f.Close() 195 return json.NewEncoder(f).Encode(mappings) 196 } 197 198 func migrateImages(root string, ls graphIDRegistrar, is image.Store, ms metadata.Store, mappings map[string]image.ID) error { 199 graphDir := filepath.Join(root, graphDirName) 200 201 dir, err := ioutil.ReadDir(graphDir) 202 if err != nil { 203 return err 204 } 205 for _, v := range dir { 206 v1ID := v.Name() 207 if err := imagev1.ValidateID(v1ID); err != nil { 208 continue 209 } 210 if _, exists := mappings[v1ID]; exists { 211 continue 212 } 213 if err := migrateImage(v1ID, root, ls, is, ms, mappings); err != nil { 214 continue 215 } 216 } 217 218 return nil 219 } 220 221 func migrateContainers(root string, ls graphIDMounter, is image.Store, imageMappings map[string]image.ID) error { 222 containersDir := filepath.Join(root, containersDirName) 223 dir, err := ioutil.ReadDir(containersDir) 224 if err != nil { 225 return err 226 } 227 for _, v := range dir { 228 id := v.Name() 229 230 if _, err := os.Stat(filepath.Join(containersDir, id, configFileName)); err == nil { 231 continue 232 } 233 234 containerJSON, err := ioutil.ReadFile(filepath.Join(containersDir, id, configFileNameLegacy)) 235 if err != nil { 236 logrus.Errorf("migrate container error: %v", err) 237 continue 238 } 239 240 var c map[string]*json.RawMessage 241 if err := json.Unmarshal(containerJSON, &c); err != nil { 242 logrus.Errorf("migrate container error: %v", err) 243 continue 244 } 245 246 imageStrJSON, ok := c["Image"] 247 if !ok { 248 return fmt.Errorf("invalid container configuration for %v", id) 249 } 250 251 var image string 252 if err := json.Unmarshal([]byte(*imageStrJSON), &image); err != nil { 253 logrus.Errorf("migrate container error: %v", err) 254 continue 255 } 256 257 imageID, ok := imageMappings[image] 258 if !ok { 259 logrus.Errorf("image not migrated %v", imageID) // non-fatal error 260 continue 261 } 262 263 c["Image"] = rawJSON(imageID) 264 265 containerJSON, err = json.Marshal(c) 266 if err != nil { 267 return err 268 } 269 270 if err := ioutil.WriteFile(filepath.Join(containersDir, id, configFileName), containerJSON, 0600); err != nil { 271 return err 272 } 273 274 img, err := is.Get(imageID) 275 if err != nil { 276 return err 277 } 278 279 if err := ls.CreateRWLayerByGraphID(id, id, img.RootFS.ChainID()); err != nil { 280 logrus.Errorf("migrate container error: %v", err) 281 continue 282 } 283 284 logrus.Infof("migrated container %s to point to %s", id, imageID) 285 286 } 287 return nil 288 } 289 290 type refAdder interface { 291 AddTag(ref reference.Named, id digest.Digest, force bool) error 292 AddDigest(ref reference.Canonical, id digest.Digest, force bool) error 293 } 294 295 func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image.ID) error { 296 migrationFile := filepath.Join(root, migrationTagsFileName) 297 if _, err := os.Lstat(migrationFile); !os.IsNotExist(err) { 298 return err 299 } 300 301 type repositories struct { 302 Repositories map[string]map[string]string 303 } 304 305 var repos repositories 306 307 f, err := os.Open(filepath.Join(root, repositoriesFilePrefixLegacy+driverName)) 308 if err != nil { 309 if os.IsNotExist(err) { 310 return nil 311 } 312 return err 313 } 314 defer f.Close() 315 if err := json.NewDecoder(f).Decode(&repos); err != nil { 316 return err 317 } 318 319 for name, repo := range repos.Repositories { 320 for tag, id := range repo { 321 if strongID, exists := mappings[id]; exists { 322 ref, err := reference.ParseNormalizedNamed(name) 323 if err != nil { 324 logrus.Errorf("migrate tags: invalid name %q, %q", name, err) 325 continue 326 } 327 if !reference.IsNameOnly(ref) { 328 logrus.Errorf("migrate tags: invalid name %q, unexpected tag or digest", name) 329 continue 330 } 331 if dgst, err := digest.Parse(tag); err == nil { 332 canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst) 333 if err != nil { 334 logrus.Errorf("migrate tags: invalid digest %q, %q", dgst, err) 335 continue 336 } 337 if err := rs.AddDigest(canonical, strongID.Digest(), false); err != nil { 338 logrus.Errorf("can't migrate digest %q for %q, err: %q", reference.FamiliarString(ref), strongID, err) 339 } 340 } else { 341 tagRef, err := reference.WithTag(ref, tag) 342 if err != nil { 343 logrus.Errorf("migrate tags: invalid tag %q, %q", tag, err) 344 continue 345 } 346 if err := rs.AddTag(tagRef, strongID.Digest(), false); err != nil { 347 logrus.Errorf("can't migrate tag %q for %q, err: %q", reference.FamiliarString(ref), strongID, err) 348 } 349 } 350 logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID) 351 } 352 } 353 } 354 355 mf, err := os.Create(migrationFile) 356 if err != nil { 357 return err 358 } 359 mf.Close() 360 361 return nil 362 } 363 364 func getParent(confDir string) (string, error) { 365 jsonFile := filepath.Join(confDir, "json") 366 imageJSON, err := ioutil.ReadFile(jsonFile) 367 if err != nil { 368 return "", err 369 } 370 var parent struct { 371 Parent string 372 ParentID digest.Digest `json:"parent_id"` 373 } 374 if err := json.Unmarshal(imageJSON, &parent); err != nil { 375 return "", err 376 } 377 if parent.Parent == "" && parent.ParentID != "" { // v1.9 378 parent.Parent = parent.ParentID.Hex() 379 } 380 // compatibilityID for parent 381 parentCompatibilityID, err := ioutil.ReadFile(filepath.Join(confDir, "parent")) 382 if err == nil && len(parentCompatibilityID) > 0 { 383 parent.Parent = string(parentCompatibilityID) 384 } 385 return parent.Parent, nil 386 } 387 388 func migrateImage(id, root string, ls graphIDRegistrar, is image.Store, ms metadata.Store, mappings map[string]image.ID) (err error) { 389 defer func() { 390 if err != nil { 391 logrus.Errorf("migration failed for %v, err: %v", id, err) 392 } 393 }() 394 395 parent, err := getParent(filepath.Join(root, graphDirName, id)) 396 if err != nil { 397 return err 398 } 399 400 var parentID image.ID 401 if parent != "" { 402 var exists bool 403 if parentID, exists = mappings[parent]; !exists { 404 if err := migrateImage(parent, root, ls, is, ms, mappings); err != nil { 405 // todo: fail or allow broken chains? 406 return err 407 } 408 parentID = mappings[parent] 409 } 410 } 411 412 rootFS := image.NewRootFS() 413 var history []image.History 414 415 if parentID != "" { 416 parentImg, err := is.Get(parentID) 417 if err != nil { 418 return err 419 } 420 421 rootFS = parentImg.RootFS 422 history = parentImg.History 423 } 424 425 diffIDData, err := ioutil.ReadFile(filepath.Join(root, graphDirName, id, migrationDiffIDFileName)) 426 if err != nil { 427 return err 428 } 429 diffID, err := digest.Parse(string(diffIDData)) 430 if err != nil { 431 return err 432 } 433 434 sizeStr, err := ioutil.ReadFile(filepath.Join(root, graphDirName, id, migrationSizeFileName)) 435 if err != nil { 436 return err 437 } 438 size, err := strconv.ParseInt(string(sizeStr), 10, 64) 439 if err != nil { 440 return err 441 } 442 443 layer, err := ls.RegisterByGraphID(id, rootFS.ChainID(), layer.DiffID(diffID), filepath.Join(root, graphDirName, id, migrationTarDataFileName), size) 444 if err != nil { 445 return err 446 } 447 logrus.Infof("migrated layer %s to %s", id, layer.DiffID()) 448 449 jsonFile := filepath.Join(root, graphDirName, id, "json") 450 imageJSON, err := ioutil.ReadFile(jsonFile) 451 if err != nil { 452 return err 453 } 454 455 h, err := imagev1.HistoryFromConfig(imageJSON, false) 456 if err != nil { 457 return err 458 } 459 history = append(history, h) 460 461 rootFS.Append(layer.DiffID()) 462 463 config, err := imagev1.MakeConfigFromV1Config(imageJSON, rootFS, history) 464 if err != nil { 465 return err 466 } 467 strongID, err := is.Create(config) 468 if err != nil { 469 return err 470 } 471 logrus.Infof("migrated image %s to %s", id, strongID) 472 473 if parentID != "" { 474 if err := is.SetParent(strongID, parentID); err != nil { 475 return err 476 } 477 } 478 479 checksum, err := ioutil.ReadFile(filepath.Join(root, graphDirName, id, "checksum")) 480 if err == nil { // best effort 481 dgst, err := digest.Parse(string(checksum)) 482 if err == nil { 483 V2MetadataService := metadata.NewV2MetadataService(ms) 484 V2MetadataService.Add(layer.DiffID(), metadata.V2Metadata{Digest: dgst}) 485 } 486 } 487 _, err = ls.Release(layer) 488 if err != nil { 489 return err 490 } 491 492 mappings[id] = strongID 493 return 494 } 495 496 func rawJSON(value interface{}) *json.RawMessage { 497 jsonval, err := json.Marshal(value) 498 if err != nil { 499 return nil 500 } 501 return (*json.RawMessage)(&jsonval) 502 }