@@ -21,6 +21,8 @@ package windows
2121import (
2222 "context"
2323 "encoding/json"
24+ "fmt"
25+ "io"
2426 "os"
2527 "path/filepath"
2628 "strconv"
@@ -29,6 +31,7 @@ import (
2931 winfs "github.com/Microsoft/go-winio/pkg/fs"
3032 "github.com/Microsoft/go-winio/vhd"
3133 "github.com/Microsoft/hcsshim"
34+ "github.com/Microsoft/hcsshim/computestorage"
3235 "github.com/containerd/containerd/errdefs"
3336 "github.com/containerd/containerd/log"
3437 "github.com/containerd/containerd/mount"
@@ -53,6 +56,9 @@ func init() {
5356}
5457
5558const (
59+ // Label to specify that we should make a scratch space for a UtilityVM.
60+ uvmScratchLabel = "containerd.io/snapshot/io.microsoft.vm.storage.scratch"
61+ // Label to control a containers scratch space size (sandbox.vhdx).
5662 rootfsSizeLabel = "containerd.io/snapshot/io.microsoft.container.storage.rootfs.size-gb"
5763)
5864
@@ -345,15 +351,6 @@ func (s *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
345351 if ! strings .Contains (key , snapshots .UnpackKeyPrefix ) {
346352 parentLayerPaths := s .parentIDsToParentPaths (newSnapshot .ParentIDs )
347353
348- var parentPath string
349- if len (parentLayerPaths ) != 0 {
350- parentPath = parentLayerPaths [0 ]
351- }
352-
353- if err := hcsshim .CreateSandboxLayer (s .info , newSnapshot .ID , parentPath , parentLayerPaths ); err != nil {
354- return nil , errors .Wrap (err , "failed to create sandbox layer" )
355- }
356-
357354 var snapshotInfo snapshots.Info
358355 for _ , o := range opts {
359356 o (& snapshotInfo )
@@ -368,12 +365,20 @@ func (s *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
368365 sizeGB = int (i32 )
369366 }
370367
371- if sizeGB > 0 {
372- const gbToByte = 1024 * 1024 * 1024
373- if err := hcsshim .ExpandSandboxSize (s .info , newSnapshot .ID , uint64 (gbToByte * sizeGB )); err != nil {
374- return nil , errors .Wrapf (err , "failed to expand scratch size to %d GB" , sizeGB )
368+ var makeUVMScratch bool
369+ if _ , ok := snapshotInfo .Labels [uvmScratchLabel ]; ok {
370+ makeUVMScratch = true
371+ }
372+
373+ // This has to be run first to avoid clashing with the containers sandbox.vhdx.
374+ if makeUVMScratch {
375+ if err := s .createUVMScratchLayer (ctx , snDir , parentLayerPaths ); err != nil {
376+ return nil , errors .Wrap (err , "failed to make UVM's scratch layer" )
375377 }
376378 }
379+ if err := s .createScratchLayer (ctx , snDir , parentLayerPaths , sizeGB ); err != nil {
380+ return nil , errors .Wrap (err , "failed to create scratch layer" )
381+ }
377382 }
378383 }
379384
@@ -391,3 +396,100 @@ func (s *snapshotter) parentIDsToParentPaths(parentIDs []string) []string {
391396 }
392397 return parentLayerPaths
393398}
399+
400+ // This is essentially a recreation of what HCS' CreateSandboxLayer does with some extra bells and
401+ // whistles like expanding the volume if a size is specified. This will create a 1GB scratch
402+ // vhdx to be used if a different sized scratch that is not equal to the default of 20 is requested.
403+ func (s * snapshotter ) createScratchLayer (ctx context.Context , snDir string , parentLayers []string , sizeGB int ) error {
404+ parentLen := len (parentLayers )
405+ if parentLen == 0 {
406+ return errors .New ("no parent layers present" )
407+ }
408+ baseLayer := parentLayers [parentLen - 1 ]
409+
410+ var (
411+ templateBase = filepath .Join (baseLayer , "blank-base.vhdx" )
412+ templateDiffDisk = filepath .Join (baseLayer , "blank.vhdx" )
413+ newDisks = sizeGB > 0 && sizeGB < 20
414+ expand = sizeGB > 0 && sizeGB != 20
415+ )
416+
417+ // If a size greater than 0 and less than 20 (the default size produced by hcs)
418+ // was specified we make a new set of disks to be used. We make it a 1GB disk and just
419+ // expand it to the size specified so for future container runs we don't need to remake a disk.
420+ if newDisks {
421+ templateBase = filepath .Join (baseLayer , "scratch.vhdx" )
422+ templateDiffDisk = filepath .Join (baseLayer , "scratch-diff.vhdx" )
423+ }
424+
425+ if _ , err := os .Stat (templateDiffDisk ); os .IsNotExist (err ) {
426+ // Scratch disk not present so lets make it.
427+ if err := computestorage .SetupContainerBaseLayer (ctx , baseLayer , templateBase , templateDiffDisk , 1 ); err != nil {
428+ return errors .Wrapf (err , "failed to create scratch vhdx at %q" , baseLayer )
429+ }
430+ }
431+
432+ dest := filepath .Join (snDir , "sandbox.vhdx" )
433+ if err := copyScratchDisk (templateDiffDisk , dest ); err != nil {
434+ return err
435+ }
436+
437+ if expand {
438+ gbToByte := 1024 * 1024 * 1024
439+ if err := hcsshim .ExpandSandboxSize (s .info , filepath .Base (snDir ), uint64 (gbToByte * sizeGB )); err != nil {
440+ return errors .Wrapf (err , "failed to expand sandbox vhdx size to %d GB" , sizeGB )
441+ }
442+ }
443+ return nil
444+ }
445+
446+ // This handles creating the UVMs scratch layer.
447+ func (s * snapshotter ) createUVMScratchLayer (ctx context.Context , snDir string , parentLayers []string ) error {
448+ parentLen := len (parentLayers )
449+ if parentLen == 0 {
450+ return errors .New ("no parent layers present" )
451+ }
452+ baseLayer := parentLayers [parentLen - 1 ]
453+
454+ // Make sure base layer has a UtilityVM folder.
455+ uvmPath := filepath .Join (baseLayer , "UtilityVM" )
456+ if _ , err := os .Stat (uvmPath ); os .IsNotExist (err ) {
457+ return errors .Wrapf (err , "failed to find UtilityVM directory in base layer %q" , baseLayer )
458+ }
459+
460+ templateDiffDisk := filepath .Join (uvmPath , "SystemTemplate.vhdx" )
461+
462+ // Check if SystemTemplate disk doesn't exist for some reason (this should be made during the unpacking
463+ // of the base layer).
464+ if _ , err := os .Stat (templateDiffDisk ); os .IsNotExist (err ) {
465+ return fmt .Errorf ("%q does not exist in Utility VM image" , templateDiffDisk )
466+ }
467+
468+ // Move the sandbox.vhdx into a nested vm folder to avoid clashing with a containers sandbox.vhdx.
469+ vmScratchDir := filepath .Join (snDir , "vm" )
470+ if err := os .MkdirAll (vmScratchDir , 0777 ); err != nil {
471+ return errors .Wrap (err , "failed to make `vm` directory for vm's scratch space" )
472+ }
473+
474+ return copyScratchDisk (templateDiffDisk , filepath .Join (vmScratchDir , "sandbox.vhdx" ))
475+ }
476+
477+ func copyScratchDisk (source , dest string ) error {
478+ scratchSource , err := os .OpenFile (source , os .O_RDWR , 0700 )
479+ if err != nil {
480+ return errors .Wrapf (err , "failed to open %s" , source )
481+ }
482+ defer scratchSource .Close ()
483+
484+ f , err := os .OpenFile (dest , os .O_RDWR | os .O_CREATE , 0700 )
485+ if err != nil {
486+ return errors .Wrap (err , "failed to create sandbox.vhdx in snapshot" )
487+ }
488+ defer f .Close ()
489+
490+ if _ , err := io .Copy (f , scratchSource ); err != nil {
491+ os .Remove (dest )
492+ return errors .Wrapf (err , "failed to copy cached %q to %q in snapshot" , source , dest )
493+ }
494+ return nil
495+ }
0 commit comments