Merge pull request #6034 from claudiubelu/windows/fixes-image-volume

Fixes Windows containers with image volumes
This commit is contained in:
Michael Crosby 2021-10-07 11:50:01 -04:00 committed by GitHub
commit 7b8a697f28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 26 deletions

View File

@ -18,6 +18,7 @@ package mount
import ( import (
"encoding/json" "encoding/json"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -50,15 +51,21 @@ func (m *Mount) Mount(target string) error {
if err = hcsshim.ActivateLayer(di, layerID); err != nil { if err = hcsshim.ActivateLayer(di, layerID); err != nil {
return errors.Wrapf(err, "failed to activate layer %s", m.Source) return errors.Wrapf(err, "failed to activate layer %s", m.Source)
} }
defer func() {
if err != nil {
hcsshim.DeactivateLayer(di, layerID)
}
}()
if err = hcsshim.PrepareLayer(di, layerID, parentLayerPaths); err != nil { if err = hcsshim.PrepareLayer(di, layerID, parentLayerPaths); err != nil {
return errors.Wrapf(err, "failed to prepare layer %s", m.Source) return errors.Wrapf(err, "failed to prepare layer %s", m.Source)
} }
// We can link the layer mount path to the given target. It is an UNC path, and it needs
// a trailing backslash.
mountPath, err := hcsshim.GetLayerMountPath(di, layerID)
if err != nil {
return errors.Wrapf(err, "failed to get layer mount path for %s", m.Source)
}
mountPath = mountPath + `\`
if err = os.Symlink(mountPath, target); err != nil {
return errors.Wrapf(err, "failed to link mount to taget %s", target)
}
return nil return nil
} }

View File

@ -20,6 +20,8 @@ import (
"context" "context"
"os" "os"
"path/filepath" "path/filepath"
goruntime "runtime"
"strings"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
@ -75,20 +77,41 @@ func WithVolumes(volumeMounts map[string]string) containerd.NewContainerOpts {
// refer to https://github.com/containerd/containerd/pull/1868 // refer to https://github.com/containerd/containerd/pull/1868
// https://github.com/containerd/containerd/pull/1785 // https://github.com/containerd/containerd/pull/1785
defer os.Remove(root) // nolint: errcheck defer os.Remove(root) // nolint: errcheck
if err := mount.All(mounts, root); err != nil {
return errors.Wrap(err, "failed to mount") unmounter := func(mountPath string) {
} if uerr := mount.Unmount(mountPath, 0); uerr != nil {
defer func() { log.G(ctx).WithError(uerr).Errorf("Failed to unmount snapshot %q", root)
if uerr := mount.Unmount(root, 0); uerr != nil {
log.G(ctx).WithError(uerr).Errorf("Failed to unmount snapshot %q", c.SnapshotKey)
if err == nil { if err == nil {
err = uerr err = uerr
} }
} }
}() }
var mountPaths []string
if goruntime.GOOS == "windows" {
for _, m := range mounts {
// appending the layerID to the root.
mountPath := filepath.Join(root, filepath.Base(m.Source))
mountPaths = append(mountPaths, mountPath)
if err := m.Mount(mountPath); err != nil {
return err
}
defer unmounter(m.Source)
}
} else {
mountPaths = append(mountPaths, root)
if err := mount.All(mounts, root); err != nil {
return errors.Wrap(err, "failed to mount")
}
defer unmounter(root)
}
for host, volume := range volumeMounts { for host, volume := range volumeMounts {
src := filepath.Join(root, volume) // The volume may have been defined with a C: prefix, which we can't use here.
volume = strings.TrimPrefix(volume, "C:")
for _, mountPath := range mountPaths {
src := filepath.Join(mountPath, volume)
if _, err := os.Stat(src); err != nil { if _, err := os.Stat(src); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
// Skip copying directory if it does not exist. // Skip copying directory if it does not exist.
@ -100,6 +123,7 @@ func WithVolumes(volumeMounts map[string]string) containerd.NewContainerOpts {
return errors.Wrap(err, "taking runtime copy of volume") return errors.Wrap(err, "taking runtime copy of volume")
} }
} }
}
return nil return nil
} }
} }

View File

@ -18,6 +18,7 @@ package opts
import ( import (
"context" "context"
"os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
@ -119,19 +120,28 @@ func WithWindowsMounts(osi osinterface.OS, config *runtime.ContainerConfig, extr
// paths, so don't use it. // paths, so don't use it.
if !namedPipePath(src) { if !namedPipePath(src) {
if _, err := osi.Stat(src); err != nil { if _, err := osi.Stat(src); err != nil {
// If the source doesn't exist, return an error instead // Create the host path if it doesn't exist. This will align
// of creating the source. This aligns with Docker's // the behavior with the Linux implementation, but it doesn't
// behavior on windows. // align with Docker's behavior on Windows.
if !os.IsNotExist(err) {
return errors.Wrapf(err, "failed to stat %q", src) return errors.Wrapf(err, "failed to stat %q", src)
} }
if err := osi.MkdirAll(src, 0755); err != nil {
return errors.Wrapf(err, "failed to mkdir %q", src)
}
}
var err error var err error
src, err = osi.ResolveSymbolicLink(src) src, err = osi.ResolveSymbolicLink(src)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to resolve symlink %q", src) return errors.Wrapf(err, "failed to resolve symlink %q", src)
} }
// hcsshim requires clean path, especially '/' -> '\'. // hcsshim requires clean path, especially '/' -> '\'. Additionally,
// for the destination, absolute paths should have the C: prefix.
src = filepath.Clean(src) src = filepath.Clean(src)
dst = filepath.Clean(dst) dst = filepath.Clean(dst)
if dst[0] == '\\' {
dst = "C:" + dst
}
} }
var options []string var options []string