Skip to content

Commit 65c575f

Browse files
Merge pull request moby#36599 from Microsoft/jjh/dockersave
LCOW: Implement `docker save`
2 parents 53683bd + 0a49de4 commit 65c575f

2 files changed

Lines changed: 96 additions & 1 deletion

File tree

daemon/graphdriver/lcow/lcow.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@
5656
package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow"
5757

5858
import (
59+
"bytes"
5960
"encoding/json"
6061
"fmt"
6162
"io"
6263
"io/ioutil"
6364
"os"
65+
"path"
6466
"path/filepath"
6567
"strconv"
6668
"strings"
@@ -964,3 +966,87 @@ func hostToGuest(hostpath string) string {
964966
func unionMountName(disks []hcsshim.MappedVirtualDisk) string {
965967
return fmt.Sprintf("%s-mount", disks[0].ContainerPath)
966968
}
969+
970+
type nopCloser struct {
971+
io.Reader
972+
}
973+
974+
func (nopCloser) Close() error {
975+
return nil
976+
}
977+
978+
type fileGetCloserFromSVM struct {
979+
id string
980+
svm *serviceVM
981+
mvd *hcsshim.MappedVirtualDisk
982+
d *Driver
983+
}
984+
985+
func (fgc *fileGetCloserFromSVM) Close() error {
986+
if fgc.svm != nil {
987+
if fgc.mvd != nil {
988+
if err := fgc.svm.hotRemoveVHDs(*fgc.mvd); err != nil {
989+
// We just log this as we're going to tear down the SVM imminently unless in global mode
990+
logrus.Errorf("failed to remove mvd %s: %s", fgc.mvd.ContainerPath, err)
991+
}
992+
}
993+
}
994+
if fgc.d != nil && fgc.svm != nil && fgc.id != "" {
995+
if err := fgc.d.terminateServiceVM(fgc.id, fmt.Sprintf("diffgetter %s", fgc.id), false); err != nil {
996+
return err
997+
}
998+
}
999+
return nil
1000+
}
1001+
1002+
func (fgc *fileGetCloserFromSVM) Get(filename string) (io.ReadCloser, error) {
1003+
errOut := &bytes.Buffer{}
1004+
outOut := &bytes.Buffer{}
1005+
file := path.Join(fgc.mvd.ContainerPath, filename)
1006+
if err := fgc.svm.runProcess(fmt.Sprintf("cat %s", file), nil, outOut, errOut); err != nil {
1007+
logrus.Debugf("cat %s failed: %s", file, errOut.String())
1008+
return nil, err
1009+
}
1010+
return nopCloser{bytes.NewReader(outOut.Bytes())}, nil
1011+
}
1012+
1013+
// DiffGetter returns a FileGetCloser that can read files from the directory that
1014+
// contains files for the layer differences. Used for direct access for tar-split.
1015+
func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
1016+
title := fmt.Sprintf("lcowdriver: diffgetter: %s", id)
1017+
logrus.Debugf(title)
1018+
1019+
ld, err := getLayerDetails(d.dir(id))
1020+
if err != nil {
1021+
logrus.Debugf("%s: failed to get vhdx information of %s: %s", title, d.dir(id), err)
1022+
return nil, err
1023+
}
1024+
1025+
// Start the SVM with a mapped virtual disk. Note that if the SVM is
1026+
// already running and we are in global mode, this will be hot-added.
1027+
mvd := hcsshim.MappedVirtualDisk{
1028+
HostPath: ld.filename,
1029+
ContainerPath: hostToGuest(ld.filename),
1030+
CreateInUtilityVM: true,
1031+
ReadOnly: true,
1032+
}
1033+
1034+
logrus.Debugf("%s: starting service VM", title)
1035+
svm, err := d.startServiceVMIfNotRunning(id, []hcsshim.MappedVirtualDisk{mvd}, fmt.Sprintf("diffgetter %s", id))
1036+
if err != nil {
1037+
return nil, err
1038+
}
1039+
1040+
logrus.Debugf("%s: waiting for svm to finish booting", title)
1041+
err = svm.getStartError()
1042+
if err != nil {
1043+
d.terminateServiceVM(id, fmt.Sprintf("diff %s", id), false)
1044+
return nil, fmt.Errorf("%s: svm failed to boot: %s", title, err)
1045+
}
1046+
1047+
return &fileGetCloserFromSVM{
1048+
id: id,
1049+
svm: svm,
1050+
mvd: &mvd,
1051+
d: d}, nil
1052+
}

image/tarexport/save.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"io/ioutil"
88
"os"
9+
"path"
910
"path/filepath"
1011
"runtime"
1112
"time"
@@ -158,6 +159,9 @@ func (l *tarexporter) takeLayerReference(id image.ID, imgDescr *imageDescriptor)
158159
if os == "" {
159160
os = runtime.GOOS
160161
}
162+
if !system.IsOSSupported(os) {
163+
return fmt.Errorf("os %q is not supported", os)
164+
}
161165
layer, err := l.lss[os].Get(topLayerID)
162166
if err != nil {
163167
return err
@@ -216,7 +220,11 @@ func (s *saveSession) save(outStream io.Writer) error {
216220
}
217221

218222
for _, l := range imageDescr.layers {
219-
layers = append(layers, filepath.Join(l, legacyLayerFileName))
223+
// IMPORTANT: We use path, not filepath here to ensure the layers
224+
// in the manifest use Unix-style forward-slashes. Otherwise, a
225+
// Linux image saved from LCOW won't be able to be imported on
226+
// LCOL.
227+
layers = append(layers, path.Join(l, legacyLayerFileName))
220228
}
221229

222230
manifest = append(manifest, manifestItem{
@@ -313,6 +321,7 @@ func (s *saveSession) saveImage(id image.ID) (map[layer.DiffID]distribution.Desc
313321
v1Img.Parent = parent.Hex()
314322
}
315323

324+
v1Img.OS = img.OS
316325
src, err := s.saveLayer(rootFS.ChainID(), v1Img, img.Created)
317326
if err != nil {
318327
return nil, err

0 commit comments

Comments
 (0)