Skip to content

Commit bde9996

Browse files
author
John Howard
committed
LCOW: ApplyDiff() use tar2ext4, not SVM
Signed-off-by: John Howard <jhoward@microsoft.com> This removes the need for an SVM in the LCOW driver to ApplyDiff. This change relates to a fix for moby#36353 However, it found another issue, tracked by moby#37955
1 parent 82a4797 commit bde9996

File tree

1 file changed

+72
-19
lines changed

1 file changed

+72
-19
lines changed

‎daemon/graphdriver/lcow/lcow.go‎

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,33 @@ import (
7171
"time"
7272

7373
"github.com/Microsoft/hcsshim"
74+
"github.com/Microsoft/hcsshim/ext4/tar2ext4"
7475
"github.com/Microsoft/opengcs/client"
7576
"github.com/docker/docker/daemon/graphdriver"
7677
"github.com/docker/docker/pkg/archive"
7778
"github.com/docker/docker/pkg/containerfs"
7879
"github.com/docker/docker/pkg/idtools"
7980
"github.com/docker/docker/pkg/ioutils"
81+
"github.com/docker/docker/pkg/reexec"
8082
"github.com/docker/docker/pkg/system"
8183
"github.com/sirupsen/logrus"
8284
)
8385

86+
// noreexec controls reexec functionality. Off by default, on for debugging purposes.
87+
var noreexec = false
88+
8489
// init registers this driver to the register. It gets initialised by the
8590
// function passed in the second parameter, implemented in this file.
8691
func init() {
8792
graphdriver.Register("lcow", InitDriver)
93+
// DOCKER_LCOW_NOREEXEC allows for inline processing which makes
94+
// debugging issues in the re-exec codepath significantly easier.
95+
if os.Getenv("DOCKER_LCOW_NOREEXEC") != "" {
96+
logrus.Warnf("LCOW Graphdriver is set to not re-exec. This is intended for debugging purposes only.")
97+
noreexec = true
98+
} else {
99+
reexec.Register("docker-lcow-tar2ext4", tar2ext4Reexec)
100+
}
88101
}
89102

90103
const (
@@ -846,32 +859,72 @@ func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) {
846859
func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
847860
logrus.Debugf("lcowdriver: applydiff: id %s", id)
848861

849-
svm, err := d.startServiceVMIfNotRunning(id, nil, fmt.Sprintf("applydiff %s", id))
862+
// Log failures here as it's undiagnosable sometimes, due to a possible panic.
863+
// See https://github.com/moby/moby/issues/37955 for more information.
864+
865+
dest := filepath.Join(d.dataRoot, id, layerFilename)
866+
if !noreexec {
867+
cmd := reexec.Command([]string{"docker-lcow-tar2ext4", dest}...)
868+
stdout := bytes.NewBuffer(nil)
869+
stderr := bytes.NewBuffer(nil)
870+
cmd.Stdin = diff
871+
cmd.Stdout = stdout
872+
cmd.Stderr = stderr
873+
874+
if err := cmd.Start(); err != nil {
875+
logrus.Warnf("lcowdriver: applydiff: id %s failed to start re-exec: %s", id, err)
876+
return 0, err
877+
}
878+
879+
if err := cmd.Wait(); err != nil {
880+
logrus.Warnf("lcowdriver: applydiff: id %s failed %s", id, err)
881+
return 0, fmt.Errorf("re-exec error: %v: stderr: %s", err, stderr)
882+
}
883+
return strconv.ParseInt(stdout.String(), 10, 64)
884+
}
885+
// The inline case
886+
size, err := tar2ext4Actual(dest, diff)
850887
if err != nil {
851-
return 0, err
888+
logrus.Warnf("lcowdriver: applydiff: id %s failed %s", id, err)
852889
}
853-
defer d.terminateServiceVM(id, fmt.Sprintf("applydiff %s", id), false)
890+
return size, err
891+
}
854892

855-
logrus.Debugf("lcowdriver: applydiff: waiting for svm to finish booting")
856-
err = svm.getStartError()
893+
// tar2ext4Reexec is the re-exec entry point for writing a layer from a tar file
894+
func tar2ext4Reexec() {
895+
size, err := tar2ext4Actual(os.Args[1], os.Stdin)
857896
if err != nil {
858-
return 0, fmt.Errorf("lcowdriver: applydiff: svm failed to boot: %s", err)
897+
fmt.Fprint(os.Stderr, err)
898+
os.Exit(1)
859899
}
900+
fmt.Fprint(os.Stdout, size)
901+
}
860902

861-
// TODO @jhowardmsft - the retries are temporary to overcome platform reliability issues.
862-
// Obviously this will be removed as platform bugs are fixed.
863-
retries := 0
864-
for {
865-
retries++
866-
size, err := svm.config.TarToVhd(filepath.Join(d.dataRoot, id, layerFilename), diff)
867-
if err != nil {
868-
if retries <= 10 {
869-
continue
870-
}
871-
return 0, err
872-
}
873-
return size, err
903+
// tar2ext4Actual is the implementation of tar2ext to write a layer from a tar file.
904+
// It can be called through re-exec (default), or inline for debugging.
905+
func tar2ext4Actual(dest string, diff io.Reader) (int64, error) {
906+
// maxDiskSize is not relating to the sandbox size - this is the
907+
// maximum possible size a layer VHD generated can be from an EXT4
908+
// layout perspective.
909+
const maxDiskSize = 128 * 1024 * 1024 * 1024 // 128GB
910+
out, err := os.Create(dest)
911+
if err != nil {
912+
return 0, err
913+
}
914+
defer out.Close()
915+
if err := tar2ext4.Convert(
916+
diff,
917+
out,
918+
tar2ext4.AppendVhdFooter,
919+
tar2ext4.ConvertWhiteout,
920+
tar2ext4.MaximumDiskSize(maxDiskSize)); err != nil {
921+
return 0, err
922+
}
923+
fi, err := os.Stat(dest)
924+
if err != nil {
925+
return 0, err
874926
}
927+
return fi.Size(), nil
875928
}
876929

877930
// Changes produces a list of changes between the specified layer

0 commit comments

Comments
 (0)