github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/migrate/v1/migratev1.go (about)

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