github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/image/tarexport/save.go (about)

     1  package tarexport
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	"github.com/docker/distribution/digest"
    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/system"
    18  	"github.com/docker/docker/reference"
    19  )
    20  
    21  type imageDescriptor struct {
    22  	refs   []reference.NamedTagged
    23  	layers []string
    24  }
    25  
    26  type saveSession struct {
    27  	*tarexporter
    28  	outDir      string
    29  	images      map[image.ID]*imageDescriptor
    30  	savedLayers map[string]struct{}
    31  }
    32  
    33  func (l *tarexporter) Save(names []string, outStream io.Writer) error {
    34  	images, err := l.parseNames(names)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	return (&saveSession{tarexporter: l, images: images}).save(outStream)
    40  }
    41  
    42  func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, error) {
    43  	imgDescr := make(map[image.ID]*imageDescriptor)
    44  
    45  	addAssoc := func(id image.ID, ref reference.Named) {
    46  		if _, ok := imgDescr[id]; !ok {
    47  			imgDescr[id] = &imageDescriptor{}
    48  		}
    49  
    50  		if ref != nil {
    51  			var tagged reference.NamedTagged
    52  			if _, ok := ref.(reference.Canonical); ok {
    53  				return
    54  			}
    55  			var ok bool
    56  			if tagged, ok = ref.(reference.NamedTagged); !ok {
    57  				var err error
    58  				if tagged, err = reference.WithTag(ref, reference.DefaultTag); err != nil {
    59  					return
    60  				}
    61  			}
    62  
    63  			for _, t := range imgDescr[id].refs {
    64  				if tagged.String() == t.String() {
    65  					return
    66  				}
    67  			}
    68  			imgDescr[id].refs = append(imgDescr[id].refs, tagged)
    69  		}
    70  	}
    71  
    72  	for _, name := range names {
    73  		ref, err := reference.ParseNamed(name)
    74  		if err != nil {
    75  			return nil, err
    76  		}
    77  		if ref.Name() == string(digest.Canonical) {
    78  			imgID, err := l.is.Search(name)
    79  			if err != nil {
    80  				return nil, err
    81  			}
    82  			addAssoc(imgID, nil)
    83  			continue
    84  		}
    85  		if reference.IsNameOnly(ref) {
    86  			assocs := l.rs.ReferencesByName(ref)
    87  			for _, assoc := range assocs {
    88  				addAssoc(assoc.ImageID, assoc.Ref)
    89  			}
    90  			if len(assocs) == 0 {
    91  				imgID, err := l.is.Search(name)
    92  				if err != nil {
    93  					return nil, err
    94  				}
    95  				addAssoc(imgID, nil)
    96  			}
    97  			continue
    98  		}
    99  		var imgID image.ID
   100  		if imgID, err = l.rs.Get(ref); err != nil {
   101  			return nil, err
   102  		}
   103  		addAssoc(imgID, ref)
   104  
   105  	}
   106  	return imgDescr, nil
   107  }
   108  
   109  func (s *saveSession) save(outStream io.Writer) error {
   110  	s.savedLayers = make(map[string]struct{})
   111  
   112  	// get image json
   113  	tempDir, err := ioutil.TempDir("", "docker-export-")
   114  	if err != nil {
   115  		return err
   116  	}
   117  	defer os.RemoveAll(tempDir)
   118  
   119  	s.outDir = tempDir
   120  	reposLegacy := make(map[string]map[string]string)
   121  
   122  	var manifest []manifestItem
   123  
   124  	for id, imageDescr := range s.images {
   125  		if err = s.saveImage(id); err != nil {
   126  			return err
   127  		}
   128  
   129  		var repoTags []string
   130  		var layers []string
   131  
   132  		for _, ref := range imageDescr.refs {
   133  			if _, ok := reposLegacy[ref.Name()]; !ok {
   134  				reposLegacy[ref.Name()] = make(map[string]string)
   135  			}
   136  			reposLegacy[ref.Name()][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1]
   137  			repoTags = append(repoTags, ref.String())
   138  		}
   139  
   140  		for _, l := range imageDescr.layers {
   141  			layers = append(layers, filepath.Join(l, legacyLayerFileName))
   142  		}
   143  
   144  		manifest = append(manifest, manifestItem{
   145  			Config:   digest.Digest(id).Hex() + ".json",
   146  			RepoTags: repoTags,
   147  			Layers:   layers,
   148  		})
   149  	}
   150  
   151  	if len(reposLegacy) > 0 {
   152  		reposFile := filepath.Join(tempDir, legacyRepositoriesFileName)
   153  		f, err := os.OpenFile(reposFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
   154  		if err != nil {
   155  			f.Close()
   156  			return err
   157  		}
   158  		if err := json.NewEncoder(f).Encode(reposLegacy); err != nil {
   159  			return err
   160  		}
   161  		if err := f.Close(); err != nil {
   162  			return err
   163  		}
   164  		if err := system.Chtimes(reposFile, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
   165  			return err
   166  		}
   167  	}
   168  
   169  	manifestFileName := filepath.Join(tempDir, manifestFileName)
   170  	f, err := os.OpenFile(manifestFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
   171  	if err != nil {
   172  		f.Close()
   173  		return err
   174  	}
   175  	if err := json.NewEncoder(f).Encode(manifest); err != nil {
   176  		return err
   177  	}
   178  	if err := f.Close(); err != nil {
   179  		return err
   180  	}
   181  	if err := system.Chtimes(manifestFileName, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
   182  		return err
   183  	}
   184  
   185  	fs, err := archive.Tar(tempDir, archive.Uncompressed)
   186  	if err != nil {
   187  		return err
   188  	}
   189  	defer fs.Close()
   190  
   191  	if _, err := io.Copy(outStream, fs); err != nil {
   192  		return err
   193  	}
   194  	return nil
   195  }
   196  
   197  func (s *saveSession) saveImage(id image.ID) error {
   198  	img, err := s.is.Get(id)
   199  	if err != nil {
   200  		return err
   201  	}
   202  
   203  	if len(img.RootFS.DiffIDs) == 0 {
   204  		return fmt.Errorf("empty export - not implemented")
   205  	}
   206  
   207  	var parent digest.Digest
   208  	var layers []string
   209  	for i := range img.RootFS.DiffIDs {
   210  		v1Img := image.V1Image{}
   211  		if i == len(img.RootFS.DiffIDs)-1 {
   212  			v1Img = img.V1Image
   213  		}
   214  		rootFS := *img.RootFS
   215  		rootFS.DiffIDs = rootFS.DiffIDs[:i+1]
   216  		v1ID, err := v1.CreateID(v1Img, rootFS.ChainID(), parent)
   217  		if err != nil {
   218  			return err
   219  		}
   220  
   221  		v1Img.ID = v1ID.Hex()
   222  		if parent != "" {
   223  			v1Img.Parent = parent.Hex()
   224  		}
   225  
   226  		if err := s.saveLayer(rootFS.ChainID(), v1Img, img.Created); err != nil {
   227  			return err
   228  		}
   229  		layers = append(layers, v1Img.ID)
   230  		parent = v1ID
   231  	}
   232  
   233  	configFile := filepath.Join(s.outDir, digest.Digest(id).Hex()+".json")
   234  	if err := ioutil.WriteFile(configFile, img.RawJSON(), 0644); err != nil {
   235  		return err
   236  	}
   237  	if err := system.Chtimes(configFile, img.Created, img.Created); err != nil {
   238  		return err
   239  	}
   240  
   241  	s.images[id].layers = layers
   242  	return nil
   243  }
   244  
   245  func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, createdTime time.Time) error {
   246  	if _, exists := s.savedLayers[legacyImg.ID]; exists {
   247  		return nil
   248  	}
   249  
   250  	outDir := filepath.Join(s.outDir, legacyImg.ID)
   251  	if err := os.Mkdir(outDir, 0755); err != nil {
   252  		return err
   253  	}
   254  
   255  	// todo: why is this version file here?
   256  	if err := ioutil.WriteFile(filepath.Join(outDir, legacyVersionFileName), []byte("1.0"), 0644); err != nil {
   257  		return err
   258  	}
   259  
   260  	imageConfig, err := json.Marshal(legacyImg)
   261  	if err != nil {
   262  		return err
   263  	}
   264  
   265  	if err := ioutil.WriteFile(filepath.Join(outDir, legacyConfigFileName), imageConfig, 0644); err != nil {
   266  		return err
   267  	}
   268  
   269  	// serialize filesystem
   270  	tarFile, err := os.Create(filepath.Join(outDir, legacyLayerFileName))
   271  	if err != nil {
   272  		return err
   273  	}
   274  	defer tarFile.Close()
   275  
   276  	l, err := s.ls.Get(id)
   277  	if err != nil {
   278  		return err
   279  	}
   280  	defer layer.ReleaseAndLog(s.ls, l)
   281  
   282  	arch, err := l.TarStream()
   283  	if err != nil {
   284  		return err
   285  	}
   286  	defer arch.Close()
   287  
   288  	if _, err := io.Copy(tarFile, arch); err != nil {
   289  		return err
   290  	}
   291  
   292  	for _, fname := range []string{"", legacyVersionFileName, legacyConfigFileName, legacyLayerFileName} {
   293  		// todo: maybe save layer created timestamp?
   294  		if err := system.Chtimes(filepath.Join(outDir, fname), createdTime, createdTime); err != nil {
   295  			return err
   296  		}
   297  	}
   298  
   299  	s.savedLayers[legacyImg.ID] = struct{}{}
   300  	return nil
   301  }