github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/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  
    11  	"github.com/Sirupsen/logrus"
    12  	"github.com/docker/docker/image"
    13  	"github.com/docker/docker/image/v1"
    14  	"github.com/docker/docker/layer"
    15  	"github.com/docker/docker/pkg/archive"
    16  	"github.com/docker/docker/pkg/chrootarchive"
    17  	"github.com/docker/docker/pkg/symlink"
    18  	"github.com/docker/docker/reference"
    19  )
    20  
    21  func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error {
    22  	tmpDir, err := ioutil.TempDir("", "docker-import-")
    23  	if err != nil {
    24  		return err
    25  	}
    26  	defer os.RemoveAll(tmpDir)
    27  
    28  	if err := chrootarchive.Untar(inTar, tmpDir, nil); err != nil {
    29  		return err
    30  	}
    31  	// read manifest, if no file then load in legacy mode
    32  	manifestPath, err := safePath(tmpDir, manifestFileName)
    33  	if err != nil {
    34  		return err
    35  	}
    36  	manifestFile, err := os.Open(manifestPath)
    37  	if err != nil {
    38  		if os.IsNotExist(err) {
    39  			return l.legacyLoad(tmpDir, outStream)
    40  		}
    41  		return manifestFile.Close()
    42  	}
    43  	defer manifestFile.Close()
    44  
    45  	var manifest []manifestItem
    46  	if err := json.NewDecoder(manifestFile).Decode(&manifest); err != nil {
    47  		return err
    48  	}
    49  
    50  	for _, m := range manifest {
    51  		configPath, err := safePath(tmpDir, m.Config)
    52  		if err != nil {
    53  			return err
    54  		}
    55  		config, err := ioutil.ReadFile(configPath)
    56  		if err != nil {
    57  			return err
    58  		}
    59  		img, err := image.NewFromJSON(config)
    60  		if err != nil {
    61  			return err
    62  		}
    63  		var rootFS image.RootFS
    64  		rootFS = *img.RootFS
    65  		rootFS.DiffIDs = nil
    66  
    67  		if expected, actual := len(m.Layers), len(img.RootFS.DiffIDs); expected != actual {
    68  			return fmt.Errorf("invalid manifest, layers length mismatch: expected %q, got %q", expected, actual)
    69  		}
    70  
    71  		for i, diffID := range img.RootFS.DiffIDs {
    72  			layerPath, err := safePath(tmpDir, m.Layers[i])
    73  			if err != nil {
    74  				return err
    75  			}
    76  			r := rootFS
    77  			r.Append(diffID)
    78  			newLayer, err := l.ls.Get(r.ChainID())
    79  			if err != nil {
    80  				newLayer, err = l.loadLayer(layerPath, rootFS)
    81  				if err != nil {
    82  					return err
    83  				}
    84  			}
    85  			defer layer.ReleaseAndLog(l.ls, newLayer)
    86  			if expected, actual := diffID, newLayer.DiffID(); expected != actual {
    87  				return fmt.Errorf("invalid diffID for layer %d: expected %q, got %q", i, expected, actual)
    88  			}
    89  			rootFS.Append(diffID)
    90  		}
    91  
    92  		imgID, err := l.is.Create(config)
    93  		if err != nil {
    94  			return err
    95  		}
    96  
    97  		for _, repoTag := range m.RepoTags {
    98  			named, err := reference.ParseNamed(repoTag)
    99  			if err != nil {
   100  				return err
   101  			}
   102  			ref, ok := named.(reference.NamedTagged)
   103  			if !ok {
   104  				return fmt.Errorf("invalid tag %q", repoTag)
   105  			}
   106  			l.setLoadedTag(ref, imgID, outStream)
   107  		}
   108  
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS) (layer.Layer, error) {
   115  	rawTar, err := os.Open(filename)
   116  	if err != nil {
   117  		logrus.Debugf("Error reading embedded tar: %v", err)
   118  		return nil, err
   119  	}
   120  	defer rawTar.Close()
   121  
   122  	inflatedLayerData, err := archive.DecompressStream(rawTar)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	defer inflatedLayerData.Close()
   127  
   128  	return l.ls.Register(inflatedLayerData, rootFS.ChainID())
   129  }
   130  
   131  func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, outStream io.Writer) error {
   132  	if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID {
   133  		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
   134  	}
   135  
   136  	if err := l.rs.AddTag(ref, imgID, true); err != nil {
   137  		return err
   138  	}
   139  	return nil
   140  }
   141  
   142  func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error {
   143  	legacyLoadedMap := make(map[string]image.ID)
   144  
   145  	dirs, err := ioutil.ReadDir(tmpDir)
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	// every dir represents an image
   151  	for _, d := range dirs {
   152  		if d.IsDir() {
   153  			if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap); err != nil {
   154  				return err
   155  			}
   156  		}
   157  	}
   158  
   159  	// load tags from repositories file
   160  	repositoriesPath, err := safePath(tmpDir, legacyRepositoriesFileName)
   161  	if err != nil {
   162  		return err
   163  	}
   164  	repositoriesFile, err := os.Open(repositoriesPath)
   165  	if err != nil {
   166  		if !os.IsNotExist(err) {
   167  			return err
   168  		}
   169  		return repositoriesFile.Close()
   170  	}
   171  	defer repositoriesFile.Close()
   172  
   173  	repositories := make(map[string]map[string]string)
   174  	if err := json.NewDecoder(repositoriesFile).Decode(&repositories); err != nil {
   175  		return err
   176  	}
   177  
   178  	for name, tagMap := range repositories {
   179  		for tag, oldID := range tagMap {
   180  			imgID, ok := legacyLoadedMap[oldID]
   181  			if !ok {
   182  				return fmt.Errorf("invalid target ID: %v", oldID)
   183  			}
   184  			named, err := reference.WithName(name)
   185  			if err != nil {
   186  				return err
   187  			}
   188  			ref, err := reference.WithTag(named, tag)
   189  			if err != nil {
   190  				return err
   191  			}
   192  			l.setLoadedTag(ref, imgID, outStream)
   193  		}
   194  	}
   195  
   196  	return nil
   197  }
   198  
   199  func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[string]image.ID) error {
   200  	if _, loaded := loadedMap[oldID]; loaded {
   201  		return nil
   202  	}
   203  	configPath, err := safePath(sourceDir, filepath.Join(oldID, legacyConfigFileName))
   204  	if err != nil {
   205  		return err
   206  	}
   207  	imageJSON, err := ioutil.ReadFile(configPath)
   208  	if err != nil {
   209  		logrus.Debugf("Error reading json: %v", err)
   210  		return err
   211  	}
   212  
   213  	var img struct{ Parent string }
   214  	if err := json.Unmarshal(imageJSON, &img); err != nil {
   215  		return err
   216  	}
   217  
   218  	var parentID image.ID
   219  	if img.Parent != "" {
   220  		for {
   221  			var loaded bool
   222  			if parentID, loaded = loadedMap[img.Parent]; !loaded {
   223  				if err := l.legacyLoadImage(img.Parent, sourceDir, loadedMap); err != nil {
   224  					return err
   225  				}
   226  			} else {
   227  				break
   228  			}
   229  		}
   230  	}
   231  
   232  	// todo: try to connect with migrate code
   233  	rootFS := image.NewRootFS()
   234  	var history []image.History
   235  
   236  	if parentID != "" {
   237  		parentImg, err := l.is.Get(parentID)
   238  		if err != nil {
   239  			return err
   240  		}
   241  
   242  		rootFS = parentImg.RootFS
   243  		history = parentImg.History
   244  	}
   245  
   246  	layerPath, err := safePath(sourceDir, filepath.Join(oldID, legacyLayerFileName))
   247  	if err != nil {
   248  		return err
   249  	}
   250  	newLayer, err := l.loadLayer(layerPath, *rootFS)
   251  	if err != nil {
   252  		return err
   253  	}
   254  	rootFS.Append(newLayer.DiffID())
   255  
   256  	h, err := v1.HistoryFromConfig(imageJSON, false)
   257  	if err != nil {
   258  		return err
   259  	}
   260  	history = append(history, h)
   261  
   262  	config, err := v1.MakeConfigFromV1Config(imageJSON, rootFS, history)
   263  	if err != nil {
   264  		return err
   265  	}
   266  	imgID, err := l.is.Create(config)
   267  	if err != nil {
   268  		return err
   269  	}
   270  
   271  	metadata, err := l.ls.Release(newLayer)
   272  	layer.LogReleaseMetadata(metadata)
   273  	if err != nil {
   274  		return err
   275  	}
   276  
   277  	if parentID != "" {
   278  		if err := l.is.SetParent(imgID, parentID); err != nil {
   279  			return err
   280  		}
   281  	}
   282  
   283  	loadedMap[oldID] = imgID
   284  	return nil
   285  }
   286  
   287  func safePath(base, path string) (string, error) {
   288  	return symlink.FollowSymlinkInScope(filepath.Join(base, path), base)
   289  }