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  }