Skip to content

Commit da7d96b

Browse files
committed
Clean up WCOW layers after tests in the correct order
This ensures that we do not trigger assertions inside HCS by tring to call hcsshim.DestroyLayer on the parent of a currently-activated layer. It also deactivates the layers before deletion, to ensure we trigger or avert file-in-use failures due to leftover state from the tests with more detail than 'destroy failed'. Signed-off-by: Paul "TBBle" Hampson <[email protected]>
1 parent 31a0f92 commit da7d96b

1 file changed

Lines changed: 66 additions & 4 deletions

File tree

sys/filesys_windows.go

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@ import (
2222
"os"
2323
"path/filepath"
2424
"regexp"
25+
"sort"
26+
"strconv"
2527
"strings"
2628
"syscall"
2729
"unsafe"
2830

2931
"github.com/Microsoft/hcsshim"
32+
"github.com/pkg/errors"
3033
"golang.org/x/sys/windows"
3134
)
3235

@@ -257,12 +260,71 @@ func windowsOpenSequential(path string, mode int, _ uint32) (fd windows.Handle,
257260
return h, e
258261
}
259262

260-
// ForceRemoveAll is the same as os.RemoveAll, but uses hcsshim.DestroyLayer in order
261-
// to delete container layers.
263+
// ForceRemoveAll is the same as os.RemoveAll, but is aware of io.containerd.snapshotter.v1.windows
264+
// and uses hcsshim to unmount and delete container layers contained therein, in the correct order,
265+
// when passed a containerd root data directory (i.e. the `--root` directory for containerd).
262266
func ForceRemoveAll(path string) error {
267+
// snapshots/windows/windows.go init()
268+
const snapshotPlugin = "io.containerd.snapshotter.v1" + "." + "windows"
269+
// snapshots/windows/windows.go NewSnapshotter()
270+
snapshotDir := filepath.Join(path, snapshotPlugin, "snapshots")
271+
if stat, err := os.Stat(snapshotDir); err == nil && stat.IsDir() {
272+
if err := cleanupWCOWLayers(snapshotDir); err != nil {
273+
return errors.Wrapf(err, "failed to cleanup WCOW layers in %s", snapshotDir)
274+
}
275+
}
276+
277+
return os.RemoveAll(path)
278+
}
279+
280+
func cleanupWCOWLayers(root string) error {
281+
// See snapshots/windows/windows.go getSnapshotDir()
282+
var layerNums []int
283+
if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
284+
if path != root && info.IsDir() {
285+
if layerNum, err := strconv.Atoi(filepath.Base(path)); err == nil {
286+
layerNums = append(layerNums, layerNum)
287+
} else {
288+
return err
289+
}
290+
return filepath.SkipDir
291+
}
292+
293+
return nil
294+
}); err != nil {
295+
return err
296+
}
297+
298+
sort.Sort(sort.Reverse(sort.IntSlice(layerNums)))
299+
300+
for _, layerNum := range layerNums {
301+
if err := cleanupWCOWLayer(filepath.Join(root, strconv.Itoa(layerNum))); err != nil {
302+
return err
303+
}
304+
}
305+
306+
return nil
307+
}
308+
309+
func cleanupWCOWLayer(layerPath string) error {
263310
info := hcsshim.DriverInfo{
264-
HomeDir: filepath.Dir(path),
311+
HomeDir: filepath.Dir(layerPath),
265312
}
266313

267-
return hcsshim.DestroyLayer(info, filepath.Base(path))
314+
// ERROR_DEV_NOT_EXIST is returned if the layer is not currently prepared.
315+
if err := hcsshim.UnprepareLayer(info, filepath.Base(layerPath)); err != nil {
316+
if hcserror, ok := err.(*hcsshim.HcsError); !ok || hcserror.Err != windows.ERROR_DEV_NOT_EXIST {
317+
return errors.Wrapf(err, "failed to unprepare %s", layerPath)
318+
}
319+
}
320+
321+
if err := hcsshim.DeactivateLayer(info, filepath.Base(layerPath)); err != nil {
322+
return errors.Wrapf(err, "failed to deactivate %s", layerPath)
323+
}
324+
325+
if err := hcsshim.DestroyLayer(info, filepath.Base(layerPath)); err != nil {
326+
return errors.Wrapf(err, "failed to destroy %s", layerPath)
327+
}
328+
329+
return nil
268330
}

0 commit comments

Comments
 (0)