mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #36599 from Microsoft/jjh/dockersave
LCOW: Implement `docker save`
This commit is contained in:
commit
65c575f592
2 changed files with 96 additions and 1 deletions
|
@ -56,11 +56,13 @@
|
|||
package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -964,3 +966,87 @@ func hostToGuest(hostpath string) string {
|
|||
func unionMountName(disks []hcsshim.MappedVirtualDisk) string {
|
||||
return fmt.Sprintf("%s-mount", disks[0].ContainerPath)
|
||||
}
|
||||
|
||||
type nopCloser struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (nopCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fileGetCloserFromSVM struct {
|
||||
id string
|
||||
svm *serviceVM
|
||||
mvd *hcsshim.MappedVirtualDisk
|
||||
d *Driver
|
||||
}
|
||||
|
||||
func (fgc *fileGetCloserFromSVM) Close() error {
|
||||
if fgc.svm != nil {
|
||||
if fgc.mvd != nil {
|
||||
if err := fgc.svm.hotRemoveVHDs(*fgc.mvd); err != nil {
|
||||
// We just log this as we're going to tear down the SVM imminently unless in global mode
|
||||
logrus.Errorf("failed to remove mvd %s: %s", fgc.mvd.ContainerPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if fgc.d != nil && fgc.svm != nil && fgc.id != "" {
|
||||
if err := fgc.d.terminateServiceVM(fgc.id, fmt.Sprintf("diffgetter %s", fgc.id), false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fgc *fileGetCloserFromSVM) Get(filename string) (io.ReadCloser, error) {
|
||||
errOut := &bytes.Buffer{}
|
||||
outOut := &bytes.Buffer{}
|
||||
file := path.Join(fgc.mvd.ContainerPath, filename)
|
||||
if err := fgc.svm.runProcess(fmt.Sprintf("cat %s", file), nil, outOut, errOut); err != nil {
|
||||
logrus.Debugf("cat %s failed: %s", file, errOut.String())
|
||||
return nil, err
|
||||
}
|
||||
return nopCloser{bytes.NewReader(outOut.Bytes())}, nil
|
||||
}
|
||||
|
||||
// DiffGetter returns a FileGetCloser that can read files from the directory that
|
||||
// contains files for the layer differences. Used for direct access for tar-split.
|
||||
func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
|
||||
title := fmt.Sprintf("lcowdriver: diffgetter: %s", id)
|
||||
logrus.Debugf(title)
|
||||
|
||||
ld, err := getLayerDetails(d.dir(id))
|
||||
if err != nil {
|
||||
logrus.Debugf("%s: failed to get vhdx information of %s: %s", title, d.dir(id), err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Start the SVM with a mapped virtual disk. Note that if the SVM is
|
||||
// already running and we are in global mode, this will be hot-added.
|
||||
mvd := hcsshim.MappedVirtualDisk{
|
||||
HostPath: ld.filename,
|
||||
ContainerPath: hostToGuest(ld.filename),
|
||||
CreateInUtilityVM: true,
|
||||
ReadOnly: true,
|
||||
}
|
||||
|
||||
logrus.Debugf("%s: starting service VM", title)
|
||||
svm, err := d.startServiceVMIfNotRunning(id, []hcsshim.MappedVirtualDisk{mvd}, fmt.Sprintf("diffgetter %s", id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logrus.Debugf("%s: waiting for svm to finish booting", title)
|
||||
err = svm.getStartError()
|
||||
if err != nil {
|
||||
d.terminateServiceVM(id, fmt.Sprintf("diff %s", id), false)
|
||||
return nil, fmt.Errorf("%s: svm failed to boot: %s", title, err)
|
||||
}
|
||||
|
||||
return &fileGetCloserFromSVM{
|
||||
id: id,
|
||||
svm: svm,
|
||||
mvd: &mvd,
|
||||
d: d}, nil
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
@ -158,6 +159,9 @@ func (l *tarexporter) takeLayerReference(id image.ID, imgDescr *imageDescriptor)
|
|||
if os == "" {
|
||||
os = runtime.GOOS
|
||||
}
|
||||
if !system.IsOSSupported(os) {
|
||||
return fmt.Errorf("os %q is not supported", os)
|
||||
}
|
||||
layer, err := l.lss[os].Get(topLayerID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -216,7 +220,11 @@ func (s *saveSession) save(outStream io.Writer) error {
|
|||
}
|
||||
|
||||
for _, l := range imageDescr.layers {
|
||||
layers = append(layers, filepath.Join(l, legacyLayerFileName))
|
||||
// IMPORTANT: We use path, not filepath here to ensure the layers
|
||||
// in the manifest use Unix-style forward-slashes. Otherwise, a
|
||||
// Linux image saved from LCOW won't be able to be imported on
|
||||
// LCOL.
|
||||
layers = append(layers, path.Join(l, legacyLayerFileName))
|
||||
}
|
||||
|
||||
manifest = append(manifest, manifestItem{
|
||||
|
@ -313,6 +321,7 @@ func (s *saveSession) saveImage(id image.ID) (map[layer.DiffID]distribution.Desc
|
|||
v1Img.Parent = parent.Hex()
|
||||
}
|
||||
|
||||
v1Img.OS = img.OS
|
||||
src, err := s.saveLayer(rootFS.ChainID(), v1Img, img.Created)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Reference in a new issue