github.com/reds/docker@v1.11.2-rc1/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  	"reflect"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	"github.com/docker/docker/image"
    14  	"github.com/docker/docker/image/v1"
    15  	"github.com/docker/docker/layer"
    16  	"github.com/docker/docker/pkg/archive"
    17  	"github.com/docker/docker/pkg/chrootarchive"
    18  	"github.com/docker/docker/pkg/progress"
    19  	"github.com/docker/docker/pkg/streamformatter"
    20  	"github.com/docker/docker/pkg/stringid"
    21  	"github.com/docker/docker/pkg/symlink"
    22  	"github.com/docker/docker/reference"
    23  )
    24  
    25  func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
    26  	var (
    27  		sf             = streamformatter.NewJSONStreamFormatter()
    28  		progressOutput progress.Output
    29  	)
    30  	if !quiet {
    31  		progressOutput = sf.NewProgressOutput(outStream, false)
    32  		outStream = &streamformatter.StdoutFormatter{Writer: outStream, StreamFormatter: streamformatter.NewJSONStreamFormatter()}
    33  	}
    34  
    35  	tmpDir, err := ioutil.TempDir("", "docker-import-")
    36  	if err != nil {
    37  		return err
    38  	}
    39  	defer os.RemoveAll(tmpDir)
    40  
    41  	if err := chrootarchive.Untar(inTar, tmpDir, nil); err != nil {
    42  		return err
    43  	}
    44  	// read manifest, if no file then load in legacy mode
    45  	manifestPath, err := safePath(tmpDir, manifestFileName)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	manifestFile, err := os.Open(manifestPath)
    50  	if err != nil {
    51  		if os.IsNotExist(err) {
    52  			return l.legacyLoad(tmpDir, outStream, progressOutput)
    53  		}
    54  		return manifestFile.Close()
    55  	}
    56  	defer manifestFile.Close()
    57  
    58  	var manifest []manifestItem
    59  	if err := json.NewDecoder(manifestFile).Decode(&manifest); err != nil {
    60  		return err
    61  	}
    62  
    63  	var parentLinks []parentLink
    64  
    65  	for _, m := range manifest {
    66  		configPath, err := safePath(tmpDir, m.Config)
    67  		if err != nil {
    68  			return err
    69  		}
    70  		config, err := ioutil.ReadFile(configPath)
    71  		if err != nil {
    72  			return err
    73  		}
    74  		img, err := image.NewFromJSON(config)
    75  		if err != nil {
    76  			return err
    77  		}
    78  		var rootFS image.RootFS
    79  		rootFS = *img.RootFS
    80  		rootFS.DiffIDs = nil
    81  
    82  		if expected, actual := len(m.Layers), len(img.RootFS.DiffIDs); expected != actual {
    83  			return fmt.Errorf("invalid manifest, layers length mismatch: expected %q, got %q", expected, actual)
    84  		}
    85  
    86  		for i, diffID := range img.RootFS.DiffIDs {
    87  			layerPath, err := safePath(tmpDir, m.Layers[i])
    88  			if err != nil {
    89  				return err
    90  			}
    91  			r := rootFS
    92  			r.Append(diffID)
    93  			newLayer, err := l.ls.Get(r.ChainID())
    94  			if err != nil {
    95  				newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), progressOutput)
    96  				if err != nil {
    97  					return err
    98  				}
    99  			}
   100  			defer layer.ReleaseAndLog(l.ls, newLayer)
   101  			if expected, actual := diffID, newLayer.DiffID(); expected != actual {
   102  				return fmt.Errorf("invalid diffID for layer %d: expected %q, got %q", i, expected, actual)
   103  			}
   104  			rootFS.Append(diffID)
   105  		}
   106  
   107  		imgID, err := l.is.Create(config)
   108  		if err != nil {
   109  			return err
   110  		}
   111  
   112  		for _, repoTag := range m.RepoTags {
   113  			named, err := reference.ParseNamed(repoTag)
   114  			if err != nil {
   115  				return err
   116  			}
   117  			ref, ok := named.(reference.NamedTagged)
   118  			if !ok {
   119  				return fmt.Errorf("invalid tag %q", repoTag)
   120  			}
   121  			l.setLoadedTag(ref, imgID, outStream)
   122  		}
   123  
   124  		parentLinks = append(parentLinks, parentLink{imgID, m.Parent})
   125  	}
   126  
   127  	for _, p := range validatedParentLinks(parentLinks) {
   128  		if p.parentID != "" {
   129  			if err := l.setParentID(p.id, p.parentID); err != nil {
   130  				return err
   131  			}
   132  		}
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  func (l *tarexporter) setParentID(id, parentID image.ID) error {
   139  	img, err := l.is.Get(id)
   140  	if err != nil {
   141  		return err
   142  	}
   143  	parent, err := l.is.Get(parentID)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	if !checkValidParent(img, parent) {
   148  		return fmt.Errorf("image %v is not a valid parent for %v", parent.ID, img.ID)
   149  	}
   150  	return l.is.SetParent(id, parentID)
   151  }
   152  
   153  func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, progressOutput progress.Output) (layer.Layer, error) {
   154  	rawTar, err := os.Open(filename)
   155  	if err != nil {
   156  		logrus.Debugf("Error reading embedded tar: %v", err)
   157  		return nil, err
   158  	}
   159  	defer rawTar.Close()
   160  
   161  	inflatedLayerData, err := archive.DecompressStream(rawTar)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	defer inflatedLayerData.Close()
   166  
   167  	if progressOutput != nil {
   168  		fileInfo, err := os.Stat(filename)
   169  		if err != nil {
   170  			logrus.Debugf("Error statting file: %v", err)
   171  			return nil, err
   172  		}
   173  
   174  		progressReader := progress.NewProgressReader(inflatedLayerData, progressOutput, fileInfo.Size(), stringid.TruncateID(id), "Loading layer")
   175  
   176  		return l.ls.Register(progressReader, rootFS.ChainID())
   177  	}
   178  	return l.ls.Register(inflatedLayerData, rootFS.ChainID())
   179  }
   180  
   181  func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, outStream io.Writer) error {
   182  	if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID {
   183  		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
   184  	}
   185  
   186  	if err := l.rs.AddTag(ref, imgID, true); err != nil {
   187  		return err
   188  	}
   189  	return nil
   190  }
   191  
   192  func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOutput progress.Output) error {
   193  	legacyLoadedMap := make(map[string]image.ID)
   194  
   195  	dirs, err := ioutil.ReadDir(tmpDir)
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	// every dir represents an image
   201  	for _, d := range dirs {
   202  		if d.IsDir() {
   203  			if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap, progressOutput); err != nil {
   204  				return err
   205  			}
   206  		}
   207  	}
   208  
   209  	// load tags from repositories file
   210  	repositoriesPath, err := safePath(tmpDir, legacyRepositoriesFileName)
   211  	if err != nil {
   212  		return err
   213  	}
   214  	repositoriesFile, err := os.Open(repositoriesPath)
   215  	if err != nil {
   216  		if !os.IsNotExist(err) {
   217  			return err
   218  		}
   219  		return repositoriesFile.Close()
   220  	}
   221  	defer repositoriesFile.Close()
   222  
   223  	repositories := make(map[string]map[string]string)
   224  	if err := json.NewDecoder(repositoriesFile).Decode(&repositories); err != nil {
   225  		return err
   226  	}
   227  
   228  	for name, tagMap := range repositories {
   229  		for tag, oldID := range tagMap {
   230  			imgID, ok := legacyLoadedMap[oldID]
   231  			if !ok {
   232  				return fmt.Errorf("invalid target ID: %v", oldID)
   233  			}
   234  			named, err := reference.WithName(name)
   235  			if err != nil {
   236  				return err
   237  			}
   238  			ref, err := reference.WithTag(named, tag)
   239  			if err != nil {
   240  				return err
   241  			}
   242  			l.setLoadedTag(ref, imgID, outStream)
   243  		}
   244  	}
   245  
   246  	return nil
   247  }
   248  
   249  func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[string]image.ID, progressOutput progress.Output) error {
   250  	if _, loaded := loadedMap[oldID]; loaded {
   251  		return nil
   252  	}
   253  	configPath, err := safePath(sourceDir, filepath.Join(oldID, legacyConfigFileName))
   254  	if err != nil {
   255  		return err
   256  	}
   257  	imageJSON, err := ioutil.ReadFile(configPath)
   258  	if err != nil {
   259  		logrus.Debugf("Error reading json: %v", err)
   260  		return err
   261  	}
   262  
   263  	var img struct{ Parent string }
   264  	if err := json.Unmarshal(imageJSON, &img); err != nil {
   265  		return err
   266  	}
   267  
   268  	var parentID image.ID
   269  	if img.Parent != "" {
   270  		for {
   271  			var loaded bool
   272  			if parentID, loaded = loadedMap[img.Parent]; !loaded {
   273  				if err := l.legacyLoadImage(img.Parent, sourceDir, loadedMap, progressOutput); err != nil {
   274  					return err
   275  				}
   276  			} else {
   277  				break
   278  			}
   279  		}
   280  	}
   281  
   282  	// todo: try to connect with migrate code
   283  	rootFS := image.NewRootFS()
   284  	var history []image.History
   285  
   286  	if parentID != "" {
   287  		parentImg, err := l.is.Get(parentID)
   288  		if err != nil {
   289  			return err
   290  		}
   291  
   292  		rootFS = parentImg.RootFS
   293  		history = parentImg.History
   294  	}
   295  
   296  	layerPath, err := safePath(sourceDir, filepath.Join(oldID, legacyLayerFileName))
   297  	if err != nil {
   298  		return err
   299  	}
   300  	newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, progressOutput)
   301  	if err != nil {
   302  		return err
   303  	}
   304  	rootFS.Append(newLayer.DiffID())
   305  
   306  	h, err := v1.HistoryFromConfig(imageJSON, false)
   307  	if err != nil {
   308  		return err
   309  	}
   310  	history = append(history, h)
   311  
   312  	config, err := v1.MakeConfigFromV1Config(imageJSON, rootFS, history)
   313  	if err != nil {
   314  		return err
   315  	}
   316  	imgID, err := l.is.Create(config)
   317  	if err != nil {
   318  		return err
   319  	}
   320  
   321  	metadata, err := l.ls.Release(newLayer)
   322  	layer.LogReleaseMetadata(metadata)
   323  	if err != nil {
   324  		return err
   325  	}
   326  
   327  	if parentID != "" {
   328  		if err := l.is.SetParent(imgID, parentID); err != nil {
   329  			return err
   330  		}
   331  	}
   332  
   333  	loadedMap[oldID] = imgID
   334  	return nil
   335  }
   336  
   337  func safePath(base, path string) (string, error) {
   338  	return symlink.FollowSymlinkInScope(filepath.Join(base, path), base)
   339  }
   340  
   341  type parentLink struct {
   342  	id, parentID image.ID
   343  }
   344  
   345  func validatedParentLinks(pl []parentLink) (ret []parentLink) {
   346  mainloop:
   347  	for i, p := range pl {
   348  		ret = append(ret, p)
   349  		for _, p2 := range pl {
   350  			if p2.id == p.parentID && p2.id != p.id {
   351  				continue mainloop
   352  			}
   353  		}
   354  		ret[i].parentID = ""
   355  	}
   356  	return
   357  }
   358  
   359  func checkValidParent(img, parent *image.Image) bool {
   360  	if len(img.History) == 0 && len(parent.History) == 0 {
   361  		return true // having history is not mandatory
   362  	}
   363  	if len(img.History)-len(parent.History) != 1 {
   364  		return false
   365  	}
   366  	for i, h := range parent.History {
   367  		if !reflect.DeepEqual(h, img.History[i]) {
   368  			return false
   369  		}
   370  	}
   371  	return true
   372  }