Repository: firecracker-microvm/firecracker-containerd
Pull request #654 (2022-05-27 16:50:09)

minor demux snapshotter metrics bug fixes
fix a scoping issues in demux snapshotter issuer. initialize monitor
properly in metrics proxy server. add metrics info to example HTTP
address resolver

Signed-off-by: Gavin Inglis <giinglis@amazon.com>

*Issue #, if available:*

*Description of changes:*


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Patch:
diff --git a/.buildkite/al2_pipeline.yml b/.buildkite/al2_pipeline.yml
index a84d139..dd9c3b7 100644
--- a/.buildkite/al2_pipeline.yml
+++ b/.buildkite/al2_pipeline.yml
@@ -34,7 +34,7 @@ steps:
       distro: "${BUILDKITE_AGENT_META_DATA_DISTRO}"
       hostname: "${BUILDKITE_AGENT_META_DATA_HOSTNAME}"
     env:
-      NUMBER_OF_VMS: "10"
+      NUMBER_OF_VMS: "100"
       EXTRAGOARGS: "-v -count=1 -timeout=1h"
     artifact_paths:
       - "runtime/logs/*"
diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml
index f0b9328..f2de5a8 100644
--- a/.buildkite/pipeline.yml
+++ b/.buildkite/pipeline.yml
@@ -97,8 +97,8 @@ steps:
       hostname: "${BUILDKITE_AGENT_META_DATA_HOSTNAME}"
     env:
       DOCKER_IMAGE_TAG: "$BUILDKITE_BUILD_NUMBER"
-      NUMBER_OF_VMS: 50
-      EXTRAGOARGS: "-v -count=1 -race"
+      NUMBER_OF_VMS: 100
+      EXTRAGOARGS: "-v -count=1 -race -timeout=1h"
       FICD_DM_VOLUME_GROUP: fcci-vg
     artifact_paths:
       - "runtime/logs/*"
diff --git a/Makefile b/Makefile
index 334dfb1..214bcde 100644
--- a/Makefile
+++ b/Makefile
@@ -411,17 +411,3 @@ $(STARGZ_BIN): $(STARGZ_DIR)/go.mod tools/stargz-builder-stamp
 	--workdir /src/$(STARGZ_DIR) \
 	localhost/$(STARGZ_BUILDER_NAME):$(DOCKER_IMAGE_TAG) \
 	make
-
-##########################
-# eStargz formatted image 
-##########################
-CTR_REMOTE_BIN=$(STARGZ_DIR)/out/ctr-remote
-DEFAULT_BASE_IMAGE=public.ecr.aws/amazonlinux/amazonlinux:latest
-DEFAULT_ESGZ_IMAGE=ghcr.io/firecracker-microvm/firecracker-containerd/amazonlinux:latest-esgz
-.PHONY: esgz-test-image push-esgz-test-image
-esgz-test-image: stargz-snapshotter
-	$(CTR_REMOTE_BIN) image pull $(DEFAULT_BASE_IMAGE)
-	$(CTR_REMOTE_BIN) image optimize --oci $(DEFAULT_BASE_IMAGE) $(DEFAULT_ESGZ_IMAGE)
-
-push-esgz-test-image:
-	$(CTR_REMOTE_BIN) image push -u $(GH_USER):$(GH_PERSONAL_ACCESS_TOKEN) $(DEFAULT_ESGZ_IMAGE) $(DEFAULT_ESGZ_IMAGE)
\ No newline at end of file
diff --git a/snapshotter/app/service.go b/snapshotter/app/service.go
index fe47168..27160a3 100644
--- a/snapshotter/app/service.go
+++ b/snapshotter/app/service.go
@@ -63,8 +63,9 @@ func Run(config config.Config) error {
 	if config.Snapshotter.Metrics.Enable {
 		sdHost := config.Snapshotter.Metrics.Host
 		sdPort := config.Snapshotter.Metrics.ServiceDiscoveryPort
-		serviceDiscovery := discovery.NewServiceDiscovery(sdHost, sdPort, cache)
-		monitor, err := initMetricsProxyMonitor(config.Snapshotter.Metrics.PortRange)
+		serviceDiscovery = discovery.NewServiceDiscovery(sdHost, sdPort, cache)
+		var err error
+		monitor, err = initMetricsProxyMonitor(config.Snapshotter.Metrics.PortRange)
 		if err != nil {
 			log.G(ctx).WithError(err).Fatal("failed creating metrics proxy monitor")
 			return err
@@ -113,19 +114,20 @@ func Run(config config.Config) error {
 			if err := snapshotter.Close(); err != nil {
 				log.G(ctx).WithError(err).Error("failed to close snapshotter")
 			}
-			if config.Snapshotter.Metrics.Enable {
-				if err := serviceDiscovery.Shutdown(ctx); err != nil {
-					log.G(ctx).WithError(err).Error("failed to shutdown service discovery server")
-				}
-				// Senders to this channel would panic if it is closed. However snapshotter.Close() will
-				// shutdown all metrics proxies and ensure there are no more senders over the channel.
-				monitor.Stop()
-			}
 		}()
 
 		for {
 			select {
 			case <-stop:
+				// cancelling context will cause shutdown to fail; shutdown before cancel
+				if config.Snapshotter.Metrics.Enable {
+					if err := serviceDiscovery.Shutdown(ctx); err != nil {
+						log.G(ctx).WithError(err).Error("failed to shutdown service discovery server")
+					}
+					// Senders to this channel would panic if it is closed. However snapshotter.Close() will
+					// shutdown all metrics proxies and ensure there are no more senders over the channel.
+					monitor.Stop()
+				}
 				cancel()
 				return nil
 			case <-ctx.Done():
diff --git a/snapshotter/demux/README.md b/snapshotter/demux/README.md
deleted file mode 100644
index dcab81a..0000000
--- a/snapshotter/demux/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# snapshotter
-
-## Building an esgz Formatted Image
-
-The demux snapshotter uses a publicly available [eStargz](https://github.com/containerd/stargz-snapshotter/blob/main/docs/estargz.md) formatted image in its integration testing pipeline. 
-This image has the reference `ghcr.io/firecracker-microvm/firecracker-containerd/amazonlinux:latest-esgz`. 
-
-### Optimize an Amazon Linux Container Image for esgz
-
-The `ctr-remote` binary is needed to optimize an image for the esgz image format. Building the stargz-snapshotter submodule provides `ctr-remote`, a `ctr` wrapper, in `_submodules/stargz-snapshotter/out/ctr-remote`.
-Refer to the [containerd stargz-snapshotter documentation on `ctr-remote`](https://github.com/containerd/stargz-snapshotter/blob/main/docs/ctr-remote.md) for additional details.
-
-The root level Makefile provides `make esgz-test-image` for pulling the latest Amazon Linux image locally and optimizing it for eStargz using `ctr-remote` into an image `ghcr.io/firecracker-microvm/firecracker-containerd/amazonlinux:latest-esgz`. 
-Code owners of `firecracker-containerd` then push the eStargz optimized image as this publicly available reference using `make push-esgz-test-image` with proper credential environment variables `GH_USER` and `GH_PERSONAL_ACCESS_TOKEN` set.
-
-### Example optimization and push to GHCR
-
-Using default base and esgz images:
-
-```bash
-$ make esgz-test-image
-$ GH_USER=xxx
-# set GH_PERSONAL_ACCESS_TOKEN with command substitution such that it does not show in shell history
-$ make \
-    GH_USER=$GH_USER \
-    GH_PERSONAL_ACCESS_TOKEN=`cat`
-    push-esgz-test-image
-# enter personal access token and CTRL^D
-```
-
-To pull, convert, and push to a repo where the user has permissions with custom base and esgz images:
-
-```bash
-$ DEFAULT_BASE_IMAGE=public.ecr.aws/amazonlinux/amazonlinux:2022
-$ DEFAULT_ESGZ_IMAGE=ghcr.io/$DESTINATION_REPO/amazonlinux:2022-esgz
-$ make \
-    DEFAULT_BASE_IMAGE=$DEFAULT_BASE_IMAGE \
-    DEFAULT_ESGZ_IMAGE=$DEFAULT_ESGZ_IMAGE \
-    esgz-test-image
-$ GH_USER=xxx
-# set GH_PERSONAL_ACCESS_TOKEN with command substitution such that it does not show in shell history
-$ make \
-    GH_USER=$GH_USER \
-    GH_PERSONAL_ACCESS_TOKEN=`cat`
-    DEFAULT_ESGZ_IMAGE=$DEFAULT_ESGZ_IMAGE \
-    push-esgz-test-image
-# enter personal access token and CTRL^D
-```
-
diff --git a/snapshotter/demux/cache/cache.go b/snapshotter/demux/cache/cache.go
index 9d1c3cb..1250144 100644
--- a/snapshotter/demux/cache/cache.go
+++ b/snapshotter/demux/cache/cache.go
@@ -16,7 +16,6 @@ package cache
 import (
 	"context"
 
-	"github.com/containerd/containerd/snapshots"
 	"github.com/firecracker-microvm/firecracker-containerd/snapshotter/demux/proxy"
 )
 
@@ -29,9 +28,6 @@ type Cache interface {
 	// fetch function if the snapshotter is not currently cached.
 	Get(ctx context.Context, key string, fetch SnapshotterProvider) (*proxy.RemoteSnapshotter, error)
 
-	// WalkAll applies the provided function across all cached snapshotters.
-	WalkAll(ctx context.Context, fn snapshots.WalkFunc, filters ...string) error
-
 	// Closes the snapshotter and removes it from the cache.
 	Evict(key string) error
 
diff --git a/snapshotter/demux/cache/snapshotter_cache.go b/snapshotter/demux/cache/snapshotter_cache.go
index 7915a92..761cbf8 100644
--- a/snapshotter/demux/cache/snapshotter_cache.go
+++ b/snapshotter/demux/cache/snapshotter_cache.go
@@ -18,7 +18,6 @@ import (
 	"fmt"
 	"sync"
 
-	"github.com/containerd/containerd/snapshots"
 	"github.com/firecracker-microvm/firecracker-containerd/snapshotter/demux/proxy"
 	"github.com/hashicorp/go-multierror"
 )
@@ -57,21 +56,6 @@ func (cache *SnapshotterCache) Get(ctx context.Context, key string, fetch Snapsh
 	return snapshotter, nil
 }
 
-// WalkAll applies the provided function to all cached snapshotters.
-func (cache *SnapshotterCache) WalkAll(ctx context.Context, fn snapshots.WalkFunc, filters ...string) error {
-	cache.mutex.RLock()
-	defer cache.mutex.RUnlock()
-
-	var allErr error
-
-	for namespace, snapshotter := range cache.snapshotters {
-		if err := snapshotter.Walk(ctx, fn, filters...); err != nil {
-			allErr = multierror.Append(allErr, fmt.Errorf("failed to walk function on snapshotter[%s]: %w", namespace, err))
-		}
-	}
-	return allErr
-}
-
 // Evict removes a cached snapshotter for a given key.
 func (cache *SnapshotterCache) Evict(key string) error {
 	cache.mutex.RLock()
diff --git a/snapshotter/demux/metrics/proxy.go b/snapshotter/demux/metrics/proxy.go
index 5c00ee2..f1b36fc 100644
--- a/snapshotter/demux/metrics/proxy.go
+++ b/snapshotter/demux/metrics/proxy.go
@@ -129,10 +129,11 @@ func NewProxy(host string, monitor *Monitor, labels map[string]string, dialer fu
 				DialContext: dialer,
 			},
 		},
-		server: nil,
-		host:   host,
-		Port:   port,
-		Labels: labels,
+		server:  nil,
+		host:    host,
+		Port:    port,
+		Labels:  labels,
+		monitor: monitor,
 	}, nil
 }
 
diff --git a/snapshotter/demux/snapshotter.go b/snapshotter/demux/snapshotter.go
index b1e55d6..345f6fc 100644
--- a/snapshotter/demux/snapshotter.go
+++ b/snapshotter/demux/snapshotter.go
@@ -243,8 +243,7 @@ func (s *Snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, filters .
 	contextLogger := log.G(ctx).WithField("function", "Walk")
 	namespace, err := getNamespaceFromContext(ctx, contextLogger)
 	if err != nil {
-		contextLogger.Debug("no namespace found, proxying walk function to all cached snapshotters")
-		return s.cache.WalkAll(ctx, fn, filters...)
+		return err
 	}
 	logger := contextLogger.WithField("namespace", namespace)
 
diff --git a/snapshotter/internal/http_address_resolver.go b/snapshotter/internal/http_address_resolver.go
index cd2c44c..ed52413 100644
--- a/snapshotter/internal/http_address_resolver.go
+++ b/snapshotter/internal/http_address_resolver.go
@@ -36,6 +36,7 @@ import (
 var (
 	port               int
 	remotePort         int
+	metricsRemotePort  int
 	containerdSockPath string
 	logger             *logrus.Logger
 )
@@ -44,6 +45,7 @@ func init() {
 	flag.IntVar(&port, "port", 10001, "service port for address resolver")
 	flag.StringVar(&containerdSockPath, "containerdSocket", "/run/firecracker-containerd/containerd.sock", "filepath to the containerd socket")
 	flag.IntVar(&remotePort, "remotePort", 10000, "the remote port on which the remote snapshotter is listening")
+	flag.IntVar(&metricsRemotePort, "metricsRemotePort", 10002, "the remote port on which the remote snapshotter metrics server is listening")
 	logger = logrus.New()
 }
 
@@ -135,6 +137,10 @@ func queryAddress(writ http.ResponseWriter, req *http.Request) {
 		Network:         "unix",
 		Address:         vmInfo.VSockPath,
 		SnapshotterPort: strconv.Itoa(remotePort),
+		MetricsPort:     strconv.Itoa(metricsRemotePort),
+		Labels: map[string]string{
+			"VMID": namespace,
+		},
 	})
 	if err != nil {
 		http.Error(writ, "Internal server error", http.StatusInternalServerError)
diff --git a/tools/image-builder/files_stargz/etc/systemd/system/metrics-socat.service b/tools/image-builder/files_stargz/etc/systemd/system/metrics-socat.service
index 9e7be8e..0b1bf33 100644
--- a/tools/image-builder/files_stargz/etc/systemd/system/metrics-socat.service
+++ b/tools/image-builder/files_stargz/etc/systemd/system/metrics-socat.service
@@ -19,4 +19,4 @@ SuccessAction=reboot
 
 [Service]
 Type=simple
-ExecStart=socat VSOCK-LISTEN:10001,reuseaddr,fork TCP:localhost:8234
+ExecStart=socat VSOCK-LISTEN:10002,reuseaddr,fork TCP:localhost:8234
diff --git a/volume/image.go b/volume/image.go
deleted file mode 100644
index 9a6f8b1..0000000
--- a/volume/image.go
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"). You may
-// not use this file except in compliance with the License. A copy of the
-// License is located at
-//
-//	http://aws.amazon.com/apache2.0/
-//
-// or in the "license" file accompanying this file. This file is distributed
-// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the License for the specific language governing
-// permissions and limitations under the License.
-
-package volume
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"os"
-	"time"
-
-	"github.com/containerd/containerd"
-	"github.com/containerd/containerd/content"
-	"github.com/containerd/containerd/images"
-	"github.com/containerd/containerd/mount"
-	"github.com/containerd/containerd/snapshots"
-	"github.com/containerd/continuity/fs"
-	"github.com/hashicorp/go-multierror"
-	"github.com/opencontainers/image-spec/identity"
-	v1 "github.com/opencontainers/image-spec/specs-go/v1"
-)
-
-type imageVolumeProvider struct {
-	client      *containerd.Client
-	image       string
-	target      string
-	snapshotter string
-	snapshot    string
-}
-
-// ImageOpt allows setting optional properties of the provider.
-type ImageOpt func(*imageVolumeProvider)
-
-// WithSnapshotter sets the snapshotter to pull images.
-func WithSnapshotter(ss string) ImageOpt {
-	return func(p *imageVolumeProvider) {
-		p.snapshotter = ss
-	}
-}
-
-func getV1ImageConfig(ctx context.Context, image containerd.Image) (*v1.Image, error) {
-	var result v1.Image
-
-	ic, err := image.Config(ctx)
-	if err != nil {
-		return nil, err
-	}
-
-	switch ic.MediaType {
-	case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
-		p, err := content.ReadBlob(ctx, image.ContentStore(), ic)
-		if err != nil {
-			return nil, err
-		}
-		if err := json.Unmarshal(p, &result); err != nil {
-			return nil, err
-		}
-	default:
-		return nil, fmt.Errorf("unknown type: %s", ic.MediaType)
-	}
-	return &result, nil
-}
-
-// FromImage returns a new provider to that expose the volumes on the given image.
-func FromImage(client *containerd.Client, image, snapshot string, opts ...ImageOpt) Provider {
-	p := imageVolumeProvider{
-		snapshotter: containerd.DefaultSnapshotter,
-		client:      client,
-		image:       image,
-		snapshot:    snapshot,
-	}
-
-	for _, opt := range opts {
-		opt(&p)
-	}
-
-	return &p
-}
-
-func (p *imageVolumeProvider) Name() string {
-	return p.image
-}
-
-func (p *imageVolumeProvider) Delete(ctx context.Context) error {
-	var retErr error
-
-	if p.target != "" {
-		retErr = mount.UnmountAll(p.target, 0)
-	}
-
-	ss := p.client.SnapshotService(p.snapshotter)
-	err := ss.Remove(ctx, p.snapshot)
-	if err != nil {
-		retErr = multierror.Append(retErr, err)
-	}
-	defer ss.Close()
-
-	return retErr
-}
-
-func (p *imageVolumeProvider) CreateVolumesUnder(ctx context.Context, tempDir string) ([]*Volume, error) {
-	image, err := p.client.GetImage(ctx, p.image)
-	if err != nil {
-		return nil, err
-	}
-
-	unpacked, err := image.IsUnpacked(ctx, p.snapshotter)
-	if err != nil {
-		return nil, err
-	}
-
-	if !unpacked {
-		err := image.Unpack(ctx, p.snapshotter)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	config, err := getV1ImageConfig(ctx, image)
-	if err != nil {
-		return nil, err
-	}
-
-	if len(config.Config.Volumes) == 0 {
-		return nil, nil
-	}
-
-	root, err := p.mountImage(ctx, tempDir)
-	if err != nil {
-		return nil, err
-	}
-	p.target = root
-
-	result := make([]*Volume, 0, len(config.Config.Volumes))
-	for path := range config.Config.Volumes {
-		hostPath, err := fs.RootPath(root, path)
-		if err != nil {
-			return nil, err
-		}
-		result = append(result, &Volume{hostPath: hostPath, containerPath: path})
-	}
-	return result, nil
-}
-
-func (p *imageVolumeProvider) mountImage(ctx context.Context, tempDir string) (string, error) {
-	image, err := p.client.GetImage(ctx, p.image)
-	if err != nil {
-		return "", err
-	}
-
-	ids, err := image.RootFS(ctx)
-	if err != nil {
-		return "", err
-	}
-
-	parent := identity.ChainID(ids).String()
-
-	ss := p.client.SnapshotService(p.snapshotter)
-	mounts, err := ss.View(ctx, p.snapshot, parent, snapshots.WithLabels(map[string]string{
-		"containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339),
-	}))
-	if err != nil {
-		return "", err
-	}
-	defer ss.Close()
-
-	target, err := os.MkdirTemp(tempDir, "mount")
-	if err != nil {
-		return "", err
-	}
-	err = mount.All(mounts, target)
-	if err != nil {
-		return "", err
-	}
-
-	return target, nil
-}
diff --git a/volume/set.go b/volume/set.go
index 369df0c..a7c7e38 100644
--- a/volume/set.go
+++ b/volume/set.go
@@ -21,7 +21,6 @@ import (
 	"path/filepath"
 
 	"github.com/containerd/containerd/containers"
-	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/mount"
 	"github.com/containerd/containerd/oci"
 	"github.com/containerd/continuity/fs"
@@ -38,24 +37,11 @@ const (
 // Set is a set of volumes.
 type Set struct {
 	volumes   map[string]*Volume
-	providers map[string][]Mount
 	tempDir   string
 	runtime   string
 	volumeDir string
 }
 
-// Provider provides volumes from different sources.
-type Provider interface {
-	// Name of the provider.
-	Name() string
-
-	// CreateVolumesUnder creates volumes under the given directory.
-	CreateVolumesUnder(ctx context.Context, tempDir string) ([]*Volume, error)
-
-	// Delete all resources made by the provider.
-	Delete(ctx context.Context) error
-}
-
 // NewSet returns a new volume set.
 func NewSet(runtime string) *Set {
 	return NewSetWithTempDir(runtime, os.TempDir())
@@ -63,45 +49,12 @@ func NewSet(runtime string) *Set {
 
 // NewSetWithTempDir returns a new volume set and creates all temporary files under the tempDir.
 func NewSetWithTempDir(runtime, tempDir string) *Set {
-	return &Set{
-		runtime:   runtime,
-		providers: make(map[string][]Mount),
-		volumes:   make(map[string]*Volume),
-		tempDir:   tempDir,
-	}
+	return &Set{runtime: runtime, volumes: make(map[string]*Volume), tempDir: tempDir}
 }
 
 // Add a volume to the set.
 func (vs *Set) Add(v *Volume) {
-	vs.volumes[fmt.Sprintf("named_%s", v.name)] = v
-}
-
-// AddFrom adds volumes from the given provider.
-func (vs *Set) AddFrom(ctx context.Context, vp Provider) error {
-	if _, exists := vs.providers[vp.Name()]; exists {
-		return fmt.Errorf("failed to add %q: %w", vp.Name(), errdefs.ErrAlreadyExists)
-	}
-	dir, err := os.MkdirTemp(vs.tempDir, "AddFrom")
-	if err != nil {
-		return err
-	}
-	volumes, err := vp.CreateVolumesUnder(ctx, dir)
-	if err != nil {
-		return err
-	}
-
-	mounts := make([]Mount, 0, len(volumes))
-	for _, v := range volumes {
-		if v.name == "" {
-			index := len(vs.volumes)
-			v.name = fmt.Sprintf("anon_%d", index)
-		}
-		vs.volumes[v.name] = v
-
-		mounts = append(mounts, Mount{key: v.name, Destination: v.containerPath, ReadOnly: false})
-	}
-	vs.providers[vp.Name()] = mounts
-	return nil
+	vs.volumes[v.name] = v
 }
 
 func (vs *Set) createDiskImage(ctx context.Context, size int64) (path string, retErr error) {
@@ -162,14 +115,11 @@ func (vs *Set) PrepareDirectory(ctx context.Context) (retErr error) {
 	}()
 
 	for _, v := range vs.volumes {
-		path, err := fs.RootPath(dir, v.name)
-		if err != nil {
-			return err
-		}
+		path := filepath.Join(dir, v.name)
 		if v.hostPath == "" {
 			continue
 		}
-		err = fs.CopyDir(path, v.hostPath)
+		err := fs.CopyDir(path, v.hostPath)
 		if err != nil {
 			retErr = fmt.Errorf("failed to copy volume %q: %w", v.name, err)
 			return
@@ -222,15 +172,11 @@ func (vs *Set) PrepareDriveMount(ctx context.Context, size int64) (dm *proto.Fir
 	}()
 
 	for _, v := range vs.volumes {
-		path, err := fs.RootPath(dir, v.name)
-		if err != nil {
-			retErr = err
-			return
-		}
+		path := filepath.Join(dir, v.name)
 		if v.hostPath == "" {
 			continue
 		}
-		err = fs.CopyDir(path, v.hostPath)
+		err := fs.CopyDir(path, v.hostPath)
 		if err != nil {
 			retErr = fmt.Errorf("failed to copy volume %q: %w", v.name, err)
 			return
@@ -255,8 +201,6 @@ type Mount struct {
 	Destination string
 	// ReadOnly is true if the volume is mounted as read-only.
 	ReadOnly bool
-
-	key string
 }
 
 // WithMounts expose given volumes to the container.
@@ -264,11 +208,7 @@ func (vs *Set) WithMounts(mountpoints []Mount) (oci.SpecOpts, error) {
 	mounts := []specs.Mount{}
 
 	for _, mp := range mountpoints {
-		key := mp.key
-		if key == "" {
-			key = fmt.Sprintf("named_%s", mp.Source)
-		}
-		v, ok := vs.volumes[key]
+		v, ok := vs.volumes[mp.Source]
 		if !ok {
 			return nil, fmt.Errorf("failed to find volume %q", mp.Source)
 		}
@@ -293,8 +233,3 @@ func (vs *Set) WithMounts(mountpoints []Mount) (oci.SpecOpts, error) {
 		return nil
 	}, nil
 }
-
-// WithMountsFromProvider exposes volumes from the provider.
-func (vs *Set) WithMountsFromProvider(name string) (oci.SpecOpts, error) {
-	return vs.WithMounts(vs.providers[name])
-}
diff --git a/volume/volume.go b/volume/volume.go
index 67d9ff2..a8a0fb1 100644
--- a/volume/volume.go
+++ b/volume/volume.go
@@ -19,9 +19,8 @@ package volume
 
 // Volume is a special directory that could be shared between containers.
 type Volume struct {
-	name          string
-	hostPath      string
-	containerPath string
+	name     string
+	hostPath string
 }
 
 // New returns an empty volume.


Test patch:
diff --git a/runtime/service_integ_test.go b/runtime/service_integ_test.go
index a7bf64d..57d4531 100644
--- a/runtime/service_integ_test.go
+++ b/runtime/service_integ_test.go
@@ -26,7 +26,6 @@ import (
 	"strconv"
 	"strings"
 	"sync"
-	"sync/atomic"
 	"syscall"
 	"testing"
 	"time"
@@ -243,12 +242,9 @@ func createTapDevice(ctx context.Context, tapName string) error {
 func TestMultipleVMs_Isolated(t *testing.T) {
 	integtest.Prepare(t)
 
-	// Seems there is a deadlock (see #581).
-	// Cancel if running VMs takes more than 5 minutes.
-	const timeout = 5 * time.Minute
+	var err error
 
 	// numberOfVmsEnvName = NUMBER_OF_VMS ENV and is configurable from buildkite
-	var err error
 	numberOfVms := defaultNumberOfVms
 	if str := os.Getenv(numberOfVmsEnvName); str != "" {
 		numberOfVms, err = strconv.Atoi(str)
@@ -256,28 +252,39 @@ func TestMultipleVMs_Isolated(t *testing.T) {
 	}
 	t.Logf("TestMultipleVMs_Isolated: will run up to %d VMs", numberOfVms)
 
-	const delta = 10
-
-	if numberOfVms <= delta {
-		ctx, cancel := context.WithTimeout(context.Background(), timeout)
-		defer cancel()
-		testMultipleVMs(ctx, t, numberOfVms)
+	// We should be able to run 10 VMs without any issues.
+	if numberOfVms <= 10 {
+		testMultipleVMs(t, 10)
 		return
 	}
 
-	// Seems the instability isn't correlating with the number of VMs.
-	// Having a failure in N VMs doesn't necessary mean running more than N VMs
-	// doesn't work at all.
-	for i := delta; i <= numberOfVms; i += delta {
-		t.Run(fmt.Sprintf("VMs=%d", i), func(t *testing.T) {
-			ctx, cancel := context.WithTimeout(context.Background(), timeout)
-			defer cancel()
-			testMultipleVMs(ctx, t, i)
+	// We have issues running 100 VMs (see #581).
+	// Incrementally increase the number of VMs to find the breaking point.
+	for i := 10; i <= numberOfVms; i += 10 {
+		success := t.Run(fmt.Sprintf("VMs=%d", i), func(t *testing.T) {
+			testMultipleVMs(t, i)
 		})
+		if !success {
+			// If running N VMs doesn't work, no point to go further.
+			return
+		}
 	}
 }
 
-func testMultipleVMs(ctx context.Context, t *testing.T, count int) {
+type Event int
+
+const (
+	Created Event = iota
+	Stopped
+)
+
+func testMultipleVMs(t *testing.T, count int) {
+	// This test starts multiple VMs and some may hit firecracker-containerd's
+	// default timeout. So overriding the timeout to wait longer.
+	// One hour should be enough to start a VM, regardless of the load of
+	// the underlying host.
+	const createVMTimeout = 1 * time.Hour
+
 	// Apparently writing a lot from Firecracker's serial console blocks VMs.
 	// https://github.com/firecracker-microvm/firecracker/blob/v1.1.0/docs/prod-host-setup.md
 	kernelArgs := integtest.DefaultRuntimeConfig.KernelArgs + " 8250.nr_uarts=0 quiet loglevel=1"
@@ -314,7 +321,7 @@ func testMultipleVMs(ctx context.Context, t *testing.T, count int) {
 		},
 	}
 
-	testCtx := namespaces.WithNamespace(ctx, defaultNamespace)
+	testCtx := namespaces.WithNamespace(context.Background(), defaultNamespace)
 
 	client, err := containerd.New(integtest.ContainerdSockPath, containerd.WithDefaultRuntime(firecrackerRuntime))
 	require.NoError(t, err, "unable to create client to containerd service at %s, is containerd running?", integtest.ContainerdSockPath)
@@ -326,17 +333,14 @@ func testMultipleVMs(ctx context.Context, t *testing.T, count int) {
 	cfg, err := config.LoadConfig("")
 	require.NoError(t, err, "failed to load config")
 
+	eventCh := make(chan Event)
+
 	// Creating tap devices without goroutines somehow stabilize this test.
 	var devices []string
 
 	defer func() {
-		// It needs a new context to delete all of the devices
-		// even if the incoming context is being cancelled.
-		ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
-		defer cancel()
-
 		for _, dev := range devices {
-			err := deleteTapDevice(ctx, dev)
+			err := deleteTapDevice(testCtx, dev)
 			assert.NoError(t, err)
 		}
 	}()
@@ -351,11 +355,6 @@ func testMultipleVMs(ctx context.Context, t *testing.T, count int) {
 		devices = append(devices, tapName)
 	}
 
-	var (
-		created int64
-		stopped int64
-	)
-
 	// This test spawns separate VMs in parallel and ensures containers are spawned within each expected VM. It asserts each
 	// container ends up in the right VM by assigning each VM a network device with a unique mac address and having each container
 	// print the mac address it sees inside its VM.
@@ -390,7 +389,7 @@ func testMultipleVMs(ctx context.Context, t *testing.T, count int) {
 				},
 				ContainerCount: containerCount,
 				JailerConfig:   jailerConfig,
-				TimeoutSeconds: 60,
+				TimeoutSeconds: uint32(createVMTimeout / time.Second),
 				// In tests, our in-VM agent has Go's race detector,
 				// which makes the agent resource-hoggy than its production build
 				// So the default VM size (128MB) is too small.
@@ -418,7 +417,7 @@ func testMultipleVMs(ctx context.Context, t *testing.T, count int) {
 					createVMErr,
 				)
 			}
-			atomic.AddInt64(&created, 1)
+			eventCh <- Created
 
 			containerEg, containerCtx := errgroup.WithContext(vmEgCtx)
 			for containerID := 0; containerID < int(containerCount); containerID++ {
@@ -479,7 +478,7 @@ func testMultipleVMs(ctx context.Context, t *testing.T, count int) {
 			}
 
 			_, err = fcClient.StopVM(ctx, &proto.StopVMRequest{VMID: strconv.Itoa(vmID), TimeoutSeconds: 5})
-			atomic.AddInt64(&stopped, 1)
+			eventCh <- Stopped
 			return err
 		}
 
@@ -495,22 +494,23 @@ func testMultipleVMs(ctx context.Context, t *testing.T, count int) {
 	ticker := time.NewTicker(10 * time.Second)
 	defer ticker.Stop()
 
-loop:
-	for {
+	var created int
+	for stopped := 0; stopped < count; {
 		select {
 		case <-vmEgCtx.Done():
-			break loop
-		case <-ticker.C:
-			c := atomic.LoadInt64(&created)
-			s := atomic.LoadInt64(&stopped)
-			t.Logf("%s: created=%d/%d stopped=%d/%d", time.Now(), c, count, s, count)
-			if s == int64(count) {
-				break loop
+			require.NoError(t, vmEg.Wait())
+			return
+		case e := <-eventCh:
+			switch e {
+			case Created:
+				created++
+			case Stopped:
+				stopped++
 			}
+		case <-ticker.C:
+			t.Logf("created=%d/%d stopped=%d/%d", created, count, stopped, count)
 		}
 	}
-
-	require.NoError(t, vmEg.Wait())
 }
 
 func testMultipleExecs(
diff --git a/runtime/volume_integ_test.go b/runtime/volume_integ_test.go
index 5df030f..c4af9d3 100644
--- a/runtime/volume_integ_test.go
+++ b/runtime/volume_integ_test.go
@@ -174,111 +174,3 @@ func testVolumes(t *testing.T, runtime string) {
 	assert.Equal(t, "hello from host\nhello from c1\nhello from c2\n", stdout.String())
 	assert.Equal(t, "", stderr.String())
 }
-
-func TestVolumeFrom_Isolated(t *testing.T) {
-	integtest.Prepare(t)
-
-	runtimes := []string{firecrackerRuntime, "io.containerd.runc.v2"}
-
-	for _, rt := range runtimes {
-		t.Run(rt, func(t *testing.T) {
-			testVolumeFrom(t, rt)
-		})
-	}
-}
-
-func testVolumeFrom(t *testing.T, runtime string) {
-	const vmID = 0
-
-	ctx := namespaces.WithNamespace(context.Background(), "default")
-
-	client, err := containerd.New(integtest.ContainerdSockPath, containerd.WithDefaultRuntime(runtime))
-	require.NoError(t, err, "unable to create client to containerd service at %s, is containerd running?", integtest.ContainerdSockPath)
-	defer client.Close()
-
-	image, err := alpineImage(ctx, client, defaultSnapshotterName)
-	require.NoError(t, err, "failed to get alpine image")
-
-	fcClient, err := integtest.NewFCControlClient(integtest.ContainerdSockPath)
-	require.NoError(t, err, "failed to create fccontrol client")
-
-	// TODO: Create and host own images with non-empty volumes.
-	ref := "docker.io/library/postgres:14.3"
-	vs := volume.NewSet(runtime)
-	provider := volume.FromImage(client, ref, "vfc-snapshot", volume.WithSnapshotter(defaultSnapshotterName))
-	defer func() {
-		err := provider.Delete(ctx)
-		require.NoError(t, err)
-	}()
-	err = vs.AddFrom(ctx, provider)
-	require.NoError(t, err)
-
-	containers := []string{"c1", "c2"}
-
-	if runtime == firecrackerRuntime {
-		mount, err := vs.PrepareDriveMount(ctx, 10*mib)
-		require.NoError(t, err)
-
-		_, err = fcClient.CreateVM(ctx, &proto.CreateVMRequest{
-			VMID:           strconv.Itoa(vmID),
-			ContainerCount: int32(len(containers)),
-			DriveMounts:    []*proto.FirecrackerDriveMount{mount},
-		})
-		require.NoError(t, err, "failed to create VM")
-	} else {
-		err := vs.PrepareDirectory(ctx)
-		require.NoError(t, err)
-	}
-
-	// Make volmes from a container.
-	volumesFromContainerImage, err := vs.WithMountsFromProvider(ref)
-	require.NoError(t, err)
-
-	dir := "/var/lib/postgresql/data"
-
-	for _, name := range containers {
-		snapshotName := fmt.Sprintf("%s-snapshot", name)
-
-		sh := fmt.Sprintf("echo hello from %s >> %s/hello.txt", name, dir)
-		container, err := client.NewContainer(ctx,
-			name,
-			containerd.WithSnapshotter(defaultSnapshotterName),
-			containerd.WithNewSnapshot(snapshotName, image),
-			containerd.WithNewSpec(
-				firecrackeroci.WithVMID(strconv.Itoa(vmID)),
-				oci.WithProcessArgs("sh", "-c", sh),
-				oci.WithDefaultPathEnv,
-				volumesFromContainerImage,
-			),
-		)
-		require.NoError(t, err, "failed to create container %s", name)
-		defer container.Delete(ctx, containerd.WithSnapshotCleanup)
-
-		result, err := integtest.RunTask(ctx, container)
-		require.NoError(t, err)
-		assert.Equalf(t, uint32(0), result.ExitCode, "stdout=%q stderr=%q", result.Stdout, result.Stderr)
-	}
-
-	name := "cat"
-	snapshotName := fmt.Sprintf("%s-snapshot", name)
-	container, err := client.NewContainer(ctx,
-		name,
-		containerd.WithSnapshotter(defaultSnapshotterName),
-		containerd.WithNewSnapshot(snapshotName, image),
-		containerd.WithNewSpec(
-			firecrackeroci.WithVMID(strconv.Itoa(vmID)),
-			oci.WithProcessArgs("cat", fmt.Sprintf("%s/hello.txt", dir)),
-			oci.WithDefaultPathEnv,
-			volumesFromContainerImage,
-		),
-	)
-	require.NoError(t, err, "failed to create container %s", name)
-	defer container.Delete(ctx, containerd.WithSnapshotCleanup)
-
-	result, err := integtest.RunTask(ctx, container)
-	require.NoError(t, err)
-
-	assert.Equal(t, uint32(0), result.ExitCode)
-	assert.Equal(t, "hello from c1\nhello from c2\n", result.Stdout)
-	assert.Equal(t, "", result.Stderr)
-}
diff --git a/snapshotter/demux/cache/snapshotter_cache_test.go b/snapshotter/demux/cache/snapshotter_cache_test.go
index bb84f45..46405eb 100644
--- a/snapshotter/demux/cache/snapshotter_cache_test.go
+++ b/snapshotter/demux/cache/snapshotter_cache_test.go
@@ -17,7 +17,6 @@ import (
 	"context"
 	"testing"
 
-	"github.com/containerd/containerd/snapshots"
 	"github.com/hashicorp/go-multierror"
 	"github.com/pkg/errors"
 
@@ -56,53 +55,13 @@ func getCachedSnapshotter(uut *SnapshotterCache) error {
 	return nil
 }
 
-func getSnapshotterPropagatesErrors(uut *SnapshotterCache) error {
+func getSnapshotterPropogatesErrors(uut *SnapshotterCache) error {
 	if _, err := uut.Get(context.Background(), "SnapshotterKey", getSnapshotterErrorFunction); err == nil {
 		return errors.New("Get function did not propagate errors from snapshotter generator function")
 	}
 	return nil
 }
 
-func successfulWalk(ctx context.Context, info snapshots.Info) error {
-	return nil
-}
-
-func applyWalkFunctionOnEmptyCache(uut *SnapshotterCache) error {
-	if err := uut.WalkAll(context.Background(), successfulWalk); err != nil {
-		return errors.New("WalkAll on empty cache incorrectly resulted in error")
-	}
-	return nil
-}
-
-func applyWalkFunctionToAllCachedSnapshotters(uut *SnapshotterCache) error {
-	if _, err := uut.Get(context.Background(), "Snapshotter-A", getSnapshotterOkFunction); err != nil {
-		return errors.Wrap(err, "Adding snapshotter A to empty cache incorrectly resulted in error")
-	}
-	if _, err := uut.Get(context.Background(), "Snapshotter-B", getSnapshotterOkFunction); err != nil {
-		return errors.Wrap(err, "Adding snapshotter B to empty cache incorrectly resulted in error")
-	}
-	if err := uut.WalkAll(context.Background(), successfulWalk); err != nil {
-		return errors.New("WalkAll on populated cache incorrectly resulted in error")
-	}
-	return nil
-}
-
-func applyWalkFunctionPropagatesErrors(uut *SnapshotterCache) error {
-	if _, err := uut.Get(context.Background(), "Snapshotter-A", getFailingSnapshotterOkFunction); err != nil {
-		return errors.Wrap(err, "Adding snapshotter A to empty cache incorrectly resulted in error")
-	}
-	// The failing snapshotter mock will fail all Walk calls before applying
-	// the snapshots.WalkFunc, but for the purposes of this test that is fine.
-	// In which case, any function will do.
-	walkFunc := func(ctx context.Context, info snapshots.Info) error {
-		return nil
-	}
-	if err := uut.WalkAll(context.Background(), walkFunc); err == nil {
-		return errors.New("WalkAll did not propagate errors from walk function")
-	}
-	return nil
-}
-
 func evictSnapshotterFromEmptyCache(uut *SnapshotterCache) error {
 	if err := uut.Evict("SnapshotterKey"); err == nil {
 		return errors.New("Evict function did not return error on call on empty cache")
@@ -121,7 +80,7 @@ func evictSnapshotterFromCache(uut *SnapshotterCache) error {
 	return nil
 }
 
-func evictSnapshotterFromCachePropagatesCloseError(uut *SnapshotterCache) error {
+func evictSnapshotterFromCachePropogatesCloseError(uut *SnapshotterCache) error {
 	if _, err := uut.Get(context.Background(), "SnapshotterKey", getFailingSnapshotterOkFunction); err != nil {
 		return errors.Wrap(err, "Adding snapshotter to empty cache incorrectly resulted in error")
 	}
@@ -197,36 +156,14 @@ func TestGetSnapshotterFromCache(t *testing.T) {
 	}{
 		{"AddSnapshotterToCache", getSnapshotterFromEmptyCache},
 		{"GetCachedSnapshotter", getCachedSnapshotter},
-		{"PropogateFetchSnapshotterErrors", getSnapshotterPropagatesErrors},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			uut := NewSnapshotterCache()
-			if err := test.run(uut); err != nil {
-				t.Fatalf("%s: %s", test.name, err.Error())
-			}
-		})
-	}
-}
-
-func TestWalkAllFunctionOnCache(t *testing.T) {
-	t.Parallel()
-
-	tests := []struct {
-		name string
-		run  func(*SnapshotterCache) error
-	}{
-		{"ApplyWalkFunctionOnEmptyCache", applyWalkFunctionOnEmptyCache},
-		{"ApplyWalkFunctionToAllCachedSnapshotters", applyWalkFunctionToAllCachedSnapshotters},
-		{"ApplyWalkFunctionPropogatesErrors", applyWalkFunctionPropagatesErrors},
+		{"PropogateFetchSnapshotterErrors", getSnapshotterPropogatesErrors},
 	}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			uut := NewSnapshotterCache()
 			if err := test.run(uut); err != nil {
-				t.Fatalf("%s: %s", test.name, err.Error())
+				t.Fatal(test.name + ": " + err.Error())
 			}
 		})
 	}
@@ -241,14 +178,14 @@ func TestEvictSnapshotterFromCache(t *testing.T) {
 	}{
 		{"EvictSnapshotterFromEmptyCache", evictSnapshotterFromEmptyCache},
 		{"EvictSnapshotterFromCache", evictSnapshotterFromCache},
-		{"PropogateEvictSnapshotterCloseErrors", evictSnapshotterFromCachePropagatesCloseError},
+		{"PropogateEvictSnapshotterCloseErrors", evictSnapshotterFromCachePropogatesCloseError},
 	}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			uut := NewSnapshotterCache()
 			if err := test.run(uut); err != nil {
-				t.Fatalf("%s: %s", test.name, err.Error())
+				t.Fatal(test.name + ": " + err.Error())
 			}
 		})
 	}
@@ -270,7 +207,7 @@ func TestCloseCache(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			uut := NewSnapshotterCache()
 			if err := test.run(uut); err != nil {
-				t.Fatalf("%s: %s", test.name, err.Error())
+				t.Fatal(test.name + ": " + err.Error())
 			}
 		})
 	}
@@ -291,7 +228,7 @@ func TestListSnapshotters(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			uut := NewSnapshotterCache()
 			if err := test.run(uut); err != nil {
-				t.Fatalf("%s: %s", test.name, err.Error())
+				t.Fatal(test.name + ": " + err.Error())
 			}
 		})
 	}
diff --git a/snapshotter/demux/snapshotter_test.go b/snapshotter/demux/snapshotter_test.go
index 2202e1a..70d20bd 100644
--- a/snapshotter/demux/snapshotter_test.go
+++ b/snapshotter/demux/snapshotter_test.go
@@ -73,27 +73,6 @@ func TestReturnErrorWhenCalledWithoutNamespacedContext(t *testing.T) {
 		{"View", func() error { _, err := uut.View(ctx, "layerKey", ""); return err }},
 		{"Commit", func() error { return uut.Commit(ctx, "layer1", "layerKey") }},
 		{"Remove", func() error { return uut.Remove(ctx, "layerKey") }},
-	}
-
-	for _, test := range tests {
-		if err := test.run(); err == nil {
-			t.Fatalf("%s call did not return error", test.name)
-		}
-	}
-}
-
-func TestNoErrorWhenCalledWithoutNamespacedContext(t *testing.T) {
-	t.Parallel()
-
-	cache := cache.NewSnapshotterCache()
-	ctx := logtest.WithT(context.Background(), t)
-
-	uut := NewSnapshotter(cache, fetchOkSnapshotter)
-
-	tests := []struct {
-		name string
-		run  func() error
-	}{
 		{"Walk", func() error {
 			var callback = func(c context.Context, i snapshots.Info) error { return nil }
 			return uut.Walk(ctx, callback)
@@ -101,8 +80,8 @@ func TestNoErrorWhenCalledWithoutNamespacedContext(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		if err := test.run(); err != nil {
-			t.Fatalf("%s call returned error on no namespace execution", test.name)
+		if err := test.run(); err == nil {
+			t.Fatal(test.name + " call did not return error")
 		}
 	}
 }
@@ -137,7 +116,7 @@ func TestReturnErrorWhenSnapshotterNotFound(t *testing.T) {
 
 	for _, test := range tests {
 		if err := test.run(); err == nil {
-			t.Fatalf("%s call did not return error", test.name)
+			t.Fatal(test.name + " call did not return error")
 		}
 	}
 }
@@ -174,7 +153,7 @@ func TestReturnErrorAfterProxyFunctionFailure(t *testing.T) {
 	for _, test := range tests {
 		t.Run(test.name+"ProxyFailure", func(t *testing.T) {
 			if err := test.run(); err == nil {
-				t.Fatalf("%s call did not return error", test.name)
+				t.Fatal(test.name + " call did not return error")
 			}
 		})
 	}
@@ -212,7 +191,7 @@ func TestNoErrorIsReturnedOnSuccessfulProxyExecution(t *testing.T) {
 	for _, test := range tests {
 		t.Run(test.name+"SuccessfulProxyCall", func(t *testing.T) {
 			if err := test.run(); err != nil {
-				t.Fatalf("%s call incorrectly returned an error", test.name)
+				t.Fatal(test.name + " call incorrectly returned an error")
 			}
 		})
 	}
diff --git a/tools/docker/Dockerfile.integ-test b/tools/docker/Dockerfile.integ-test
index 728b237..6e56f02 100644
--- a/tools/docker/Dockerfile.integ-test
+++ b/tools/docker/Dockerfile.integ-test
@@ -46,8 +46,7 @@ RUN --mount=type=bind,target=/src \
   ln -sv /usr/local/bin/firecracker-ctr /usr/local/bin/ctr
 RUN containerd 2>/dev/null & \
   ctr --address /run/firecracker-containerd/containerd.sock content fetch docker.io/library/alpine:3.10.1 >/dev/null && \
-  ctr --address /run/firecracker-containerd/containerd.sock content fetch docker.io/mlabbe/iperf3:3.6-r0 >/dev/null && \
-  ctr --address /run/firecracker-containerd/containerd.sock content fetch docker.io/library/postgres:14.3 >/dev/null
+  ctr --address /run/firecracker-containerd/containerd.sock content fetch docker.io/mlabbe/iperf3:3.6-r0 >/dev/null
 
 # Install critest
 ENV VERSION="v1.23.0"
diff --git a/volume/set_test.go b/volume/set_test.go
deleted file mode 100644
index 14a5d6a..0000000
--- a/volume/set_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"). You may
-// not use this file except in compliance with the License. A copy of the
-// License is located at
-//
-//	http://aws.amazon.com/apache2.0/
-//
-// or in the "license" file accompanying this file. This file is distributed
-// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the License for the specific language governing
-// permissions and limitations under the License.
-
-package volume
-
-import (
-	"context"
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-type testProvider struct {
-	name string
-}
-
-func (p *testProvider) CreateVolumesUnder(ctx context.Context, path string) ([]*Volume, error) {
-	return nil, nil
-}
-func (p *testProvider) Delete(ctx context.Context) error {
-	return nil
-}
-func (p *testProvider) Name() string {
-	return p.name
-}
-
-func TestNewSet(t *testing.T) {
-	provider := &testProvider{name: "foobar"}
-
-	ctx := context.Background()
-	vs := NewSet("")
-	err := vs.AddFrom(ctx, provider)
-	require.NoError(t, err)
-}