Merge pull request #4415 from TBBle/wcow_commit_layers

Commit writable WCOW layers as read-only parent layers
This commit is contained in:
Akihiro Suda 2021-04-19 14:35:08 +09:00 committed by GitHub
commit bbbd851381
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -23,15 +23,18 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"github.com/Microsoft/go-winio"
winfs "github.com/Microsoft/go-winio/pkg/fs" winfs "github.com/Microsoft/go-winio/pkg/fs"
"github.com/Microsoft/go-winio/vhd" "github.com/Microsoft/go-winio/vhd"
"github.com/Microsoft/hcsshim" "github.com/Microsoft/hcsshim"
"github.com/Microsoft/hcsshim/computestorage" "github.com/Microsoft/hcsshim/computestorage"
"github.com/Microsoft/hcsshim/pkg/ociwclayer"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
@ -185,14 +188,14 @@ func (s *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, er
return s.mounts(snapshot), nil return s.mounts(snapshot), nil
} }
func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error { func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) (retErr error) {
ctx, t, err := s.ms.TransactionContext(ctx, true) ctx, t, err := s.ms.TransactionContext(ctx, true)
if err != nil { if err != nil {
return err return err
} }
defer func() { defer func() {
if err != nil { if retErr != nil {
if rerr := t.Rollback(); rerr != nil { if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("failed to rollback transaction") log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
} }
@ -202,15 +205,30 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
// grab the existing id // grab the existing id
id, _, _, err := storage.GetInfo(ctx, key) id, _, _, err := storage.GetInfo(ctx, key)
if err != nil { if err != nil {
return err return errors.Wrapf(err, "failed to get storage info for %s", key)
} }
usage, err := fs.DiskUsage(ctx, s.getSnapshotDir(id)) snapshot, err := storage.GetSnapshot(ctx, key)
if err != nil { if err != nil {
return err return err
} }
if _, err = storage.CommitActive(ctx, key, name, snapshots.Usage(usage), opts...); err != nil { path := s.getSnapshotDir(id)
// If (windowsDiff).Apply was used to populate this layer, then it's already in the 'committed' state.
// See createSnapshot below for more details
if !strings.Contains(key, snapshots.UnpackKeyPrefix) {
if err := s.convertScratchToReadOnlyLayer(ctx, snapshot, path); err != nil {
return err
}
}
usage, err := fs.DiskUsage(ctx, path)
if err != nil {
return errors.Wrapf(err, "failed to collect disk usage of snapshot storage: %s", path)
}
if _, err := storage.CommitActive(ctx, key, name, snapshots.Usage(usage), opts...); err != nil {
return errors.Wrap(err, "failed to commit snapshot") return errors.Wrap(err, "failed to commit snapshot")
} }
return t.Commit() return t.Commit()
@ -443,6 +461,43 @@ func (s *snapshotter) createScratchLayer(ctx context.Context, snDir string, pare
return nil return nil
} }
// convertScratchToReadOnlyLayer reimporst the layer over itself, to transfer the files from the sandbox.vhdx to the on-disk storage.
func (s *snapshotter) convertScratchToReadOnlyLayer(ctx context.Context, snapshot storage.Snapshot, path string) (retErr error) {
// TODO darrenstahlmsft: When this is done isolated, we should disable these.
// it currently cannot be disabled, unless we add ref counting. Since this is
// temporary, leaving it enabled is OK for now.
// https://github.com/containerd/containerd/issues/1681
if err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}); err != nil {
return errors.Wrap(err, "failed to enable necessary privileges")
}
parentLayerPaths := s.parentIDsToParentPaths(snapshot.ParentIDs)
reader, writer := io.Pipe()
go func() {
err := ociwclayer.ExportLayerToTar(ctx, writer, path, parentLayerPaths)
writer.CloseWithError(err)
}()
if _, err := ociwclayer.ImportLayerFromTar(ctx, reader, path, parentLayerPaths); err != nil {
return errors.Wrap(err, "failed to reimport snapshot")
}
if _, err := io.Copy(ioutil.Discard, reader); err != nil {
return errors.Wrap(err, "failed discarding extra data in import stream")
}
// NOTE: We do not delete the sandbox.vhdx here, as that will break later calls to
// ociwclayer.ExportLayerToTar for this snapshot.
// As a consequence, the data for this layer is held twice, once on-disk and once
// in the sandbox.vhdx.
// TODO: This is either a bug or misfeature in hcsshim, so will need to be resolved
// there first.
return nil
}
// This handles creating the UVMs scratch layer. // This handles creating the UVMs scratch layer.
func (s *snapshotter) createUVMScratchLayer(ctx context.Context, snDir string, parentLayers []string) error { func (s *snapshotter) createUVMScratchLayer(ctx context.Context, snDir string, parentLayers []string) error {
parentLen := len(parentLayers) parentLen := len(parentLayers)