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