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